/**
 * Lista de Itens — router, sessão, dados e acções.
 *
 * As rotas vivem no hash (#/mes?item=941, #/base?sec=clientes) para que se
 * possam enviar por link. Desde a Fase 2 os dados vêm da API (GET /api/base):
 * cada acção chama a API, que grava, regista quem e quando e publica no Teams;
 * depois a base volta a ser pedida. A cada minuto também, para se ver o que os
 * outros mudaram. Desenho em docs/fase-2-desenho.md.
 */

const ContextoLista = React.createContext(null);
function useLista() { return React.useContext(ContextoLista); }

const ROTAS = [
  { chave: 'resumo', rotulo: 'Resumo', Ecra: ScreenResumo },
  { chave: 'mes', rotulo: 'Por mês', Ecra: ScreenPorMes },
  { chave: 'pendentes', rotulo: 'Pendentes de instalação', Ecra: ScreenPendentes },
  { chave: 'tarefas', rotulo: 'Tarefas', Ecra: ScreenTarefas },
  { chave: 'base', rotulo: 'Base de dados', Ecra: ScreenBase },
];
const ROTAS_SEM_MENU = ['novo', 'editar', 'pessoa'];
const RECARREGAR_MS = 60 * 1000;

function lerLocal() {
  const [caminho, qs] = location.hash.replace(/^#\/?/, '').split('?');
  const p = new URLSearchParams(qs || '');
  return {
    rota: caminho || 'resumo',
    item: p.get('item') ? Number(p.get('item')) : null,
    sec: p.get('sec'),
    id: p.get('id') ? Number(p.get('id')) : null,
  };
}

function Cabecalho({ local, contagens, pesquisa, setPesquisa, utilizador, onSair }) {
  // No telemóvel a barra de separadores rola: o separador ativo tem de ficar à vista
  React.useEffect(() => {
    const a = document.querySelector('nav[aria-label="Secções"] [aria-current="page"]');
    if (a && a.scrollIntoView) a.scrollIntoView({ block: 'nearest', inline: 'center' });
  }, [local.rota]);
  // ...e os que ficam fora do ecrã à direita têm de se adivinhar: a barra esbate-se enquanto houver mais
  const refNav = React.useRef(null);
  const [haMais, setHaMais] = React.useState(false);
  React.useEffect(() => {
    const nav = refNav.current;
    if (!nav) return undefined;
    const medir = () => setHaMais(nav.scrollLeft + nav.clientWidth < nav.scrollWidth - 4);
    medir();
    nav.addEventListener('scroll', medir, { passive: true });
    window.addEventListener('resize', medir);
    return () => { nav.removeEventListener('scroll', medir); window.removeEventListener('resize', medir); };
  }, []);
  // Letra maior (o Bruno achou a vista pequena, 28/09): fica guardado neste browser
  const [letraGrande, setLetraGrande] = React.useState(() => lerPreferencia('letra', '') === 'grande');
  React.useEffect(() => {
    document.documentElement.style.fontSize = letraGrande ? '112.5%' : '';
    guardarPreferencia('letra', letraGrande ? 'grande' : 'normal');
  }, [letraGrande]);
  const hoje = HOJE.toLocaleDateString('pt-PT', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
  return (
    <header className="sticky top-0 z-40 border-b border-border bg-background/95 backdrop-blur">
      <div className="mx-auto flex max-w-7xl flex-wrap items-center gap-x-6 gap-y-3 px-4 pt-4 md:px-6">
        <a href="#/resumo" className="flex items-center gap-3">
          <img src="/assets/simbolo-netscreen.svg" alt="" width="36" height="26" />
          <span>
            <span className="block font-semibold leading-tight">Lista de Itens</span>
            <span className="block text-xs text-muted-foreground">Hoje, {hoje}</span>
          </span>
        </a>
        <div className="ml-auto flex w-full flex-wrap items-center gap-3 sm:w-auto">
          <label className="relative min-w-0 flex-1 sm:w-64 sm:flex-none">
            <span className="sr-only">Procurar itens</span>
            <IconProcurar size={16} className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
            <input type="search" className="campo !pl-9" placeholder="Nº, cliente ou equipamento" value={pesquisa} onChange={(e) => setPesquisa(e.target.value)} />
          </label>
          <span className="text-sm leading-tight">
            <span className="block">{utilizador.nome}</span>
            <span className="block text-xs text-muted-foreground">{ROTULO_PERFIL[utilizador.perfil]}</span>
          </span>
          <button
            type="button"
            className={`${BOTAO_DISCRETO} ${letraGrande ? '!text-foreground' : ''}`}
            onClick={() => setLetraGrande((v) => !v)}
            aria-pressed={letraGrande}
            title={letraGrande ? 'Voltar ao tamanho normal' : 'Letra maior'}
          >
            <span aria-hidden="true">A<span className="text-base">A</span></span>
            <span className="sr-only">Letra maior</span>
          </button>
          <button type="button" className={BOTAO_DISCRETO} onClick={onSair}>Sair</button>
          {utilizador.perfil === 'admin' && (
            <a href="#/novo" className={BOTAO_PRIMARIO}><IconMais size={14} /> Novo item</a>
          )}
        </div>
        <nav ref={refNav} className={`sem-barra -mb-px flex w-full gap-1 overflow-x-auto ${haMais ? 'ha-mais' : ''}`} aria-label="Secções">
          {ROTAS.map((r) => (
            <a
              key={r.chave}
              href={`#/${r.chave}`}
              aria-current={local.rota === r.chave ? 'page' : undefined}
              className="nav-topo whitespace-nowrap px-3 py-3 text-sm text-muted-foreground hover:text-foreground"
            >
              {r.rotulo}
              {contagens[r.chave] != null && <span className="ml-1.5 tabular-nums text-muted-foreground">{contagens[r.chave]}</span>}
            </a>
          ))}
        </nav>
      </div>
    </header>
  );
}

function EcraACarregar({ texto = 'A carregar a lista' }) {
  return <div className="flex min-h-screen items-center justify-center text-sm text-muted-foreground">{texto}</div>;
}

function App() {
  const [me, setMe] = React.useState(undefined);      // undefined = a verificar; null = sem sessão
  const [dados, setDados] = React.useState(null);
  const [erroBase, setErroBase] = React.useState('');
  const [local, setLocal] = React.useState(lerLocal);
  const [pesquisa, setPesquisa] = React.useState('');
  const [toast, setToast] = React.useState(null);

  React.useEffect(() => {
    const aoMudar = () => setLocal(lerLocal());
    window.addEventListener('hashchange', aoMudar);
    return () => window.removeEventListener('hashchange', aoMudar);
  }, []);

  async function carregarSessao() {
    try {
      setMe(await api.get('/api/me'));
    } catch (err) {
      setMe(null);
    }
  }

  async function carregarBase() {
    try {
      // Um servidor ainda sem cabines (antes da migração 002) não parte o ecrã
      setDados(Object.assign({ cabines: [] }, await api.get('/api/base')));
      setErroBase('');
    } catch (err) {
      if (err.status === 401) { setMe(null); setDados(null); return; }
      setErroBase(err.message);
    }
  }

  React.useEffect(() => { carregarSessao(); }, []);
  React.useEffect(() => {
    if (!me) return undefined;
    carregarBase();
    // O que os outros mudaram aparece sozinho, sem recarregar a página
    const t = setInterval(() => { if (document.visibilityState === 'visible') carregarBase(); }, RECARREGAR_MS);
    return () => clearInterval(t);
  }, [me && me.id]);

  const itens = React.useMemo(() => {
    if (!dados) return [];
    const q = paraPesquisa(pesquisa.trim());
    return dados.itens.map(analisarItem).filter((a) => {
      if (!q) return true;
      const texto = paraPesquisa([a.numero, a.cliente, a.clienteNumero].concat(a.comerciais, a.equipamentos.flatMap((e) => [e.descricao].concat(e.series))).join(' '));
      return texto.includes(q);
    });
  }, [dados, pesquisa]);

  if (me === undefined) return <EcraACarregar texto="A abrir" />;
  if (me === null) return <ScreenLogin onEntrar={carregarSessao} />;
  if (!dados) return <EcraACarregar texto={erroBase || 'A carregar a lista'} />;

  const utilizador = me;
  const admin = utilizador.perfil === 'admin';

  function irPara(rota, item, extra) {
    const p = new URLSearchParams(extra || {});
    if (item) p.set('item', item);
    const qs = p.toString();
    location.hash = `/${rota}${qs ? `?${qs}` : ''}`;
  }
  function abrirItem(numero) {
    const rota = ROTAS_SEM_MENU.includes(local.rota) ? 'resumo' : local.rota;
    irPara(rota, numero, local.sec ? { sec: local.sec } : null);
  }

  function avisar(mensagem) { setToast({ id: Date.now(), mensagem }); }

  /** O aviso depois de uma alteração: o que foi publicado no Teams, ou que ficou só no registo. */
  function avisarNotificacao(n) {
    if (!n) return;
    avisar(
      <span className="flex gap-3">
        <IconSino size={16} className="mt-0.5 shrink-0 text-muted-foreground" />
        <span>
          <span className="block">{n.titulo}</span>
          <span className="block text-xs text-muted-foreground">
            {n.enviado ? 'Publicado no grupo do Teams "Lista de Itens".' : 'Gravado. O Teams ainda não está ligado: a notificação ficou no registo do servidor.'}
          </span>
        </span>
      </span>
    );
  }

  /**
   * Corre uma chamada à API, recarrega a base e avisa. Devolve true se correu bem
   * (os formulários só fecham ou mudam de página nesse caso).
   */
  async function executar(chamada, aviso) {
    try {
      const r = await chamada();
      await carregarBase();
      if (r && r.notificacao) avisarNotificacao(r.notificacao);
      else if (aviso) avisar(typeof aviso === 'function' ? aviso(r) : aviso);
      return r || true;
    } catch (err) {
      if (err.status === 401) { setMe(null); return false; }
      avisar(<span className="text-[hsl(var(--offline))]">{err.message}</span>);
      await carregarBase();
      return false;
    }
  }

  const url = (numero, resto = '') => `/api/itens/${numero}${resto}`;
  const acoes = {
    concluirEtapa: (numero, idx, data) => executar(() => api.post(url(numero, `/etapas/${idx}/concluir`), { data })),
    reabrirEtapa: (numero, idx) => executar(() => api.post(url(numero, `/etapas/${idx}/reabrir`)), 'Etapa reaberta.'),
    alterarPrevista: (numero, idx, prevista) => executar(() => api.post(url(numero, `/etapas/${idx}/prevista`), { prevista })),
    alternarSubtarefa: (numero, idx, si) => executar(() => api.post(url(numero, `/etapas/${idx}/subtarefas/${si}`)), 'Subtarefa atualizada.'),
    mudarEstado: (numero, estado, motivo) => executar(() => api.post(url(numero, '/estado'), { estado, motivo })),
    mudarChamada: (numero, valor) => executar(() => api.post(url(numero, '/chamada'), { valor }), 'Chamada de atenção retirada.'),
    validarItem: (numero) => executar(() => api.post(url(numero, '/validar'))),
    criarItem: (corpo) => executar(() => api.post('/api/itens', corpo)),
    editarItem: (numero, corpo) => executar(() => api.put(url(numero), corpo), 'Item atualizado.'),
    // ── Base de dados ──
    guardarCliente: (original, novo) => executar(
      () => (original ? api.put(`/api/clientes/${original.id}`, novo) : api.post('/api/clientes', novo)),
      original ? 'Cliente atualizado.' : 'Cliente criado.'
    ),
    apagarCliente: (original) => executar(() => api.del(`/api/clientes/${original.id}`), 'Cliente apagado.'),
    juntarClientes: (clientes, resultado) => executar(
      () => api.post('/api/clientes/juntar', { ids: clientes.map((c) => c.id), resultado }),
      (r) => `${plural(clientes.length, 'cliente', 'clientes')} juntos em "${resultado.nome}"${r.itens ? ` (${plural(r.itens, 'item atualizado', 'itens atualizados')})` : ''}.`
    ),
    guardarEquipamento: (original, novo) => executar(
      () => (original ? api.put(`/api/equipamentos/${original.id}`, novo) : api.post('/api/equipamentos', novo)),
      original ? 'Equipamento atualizado.' : 'Equipamento criado.'
    ),
    apagarEquipamento: (original) => executar(() => api.del(`/api/equipamentos/${original.id}`), 'Equipamento apagado.'),
    juntarEquipamentos: (equipamentos, resultado) => executar(
      () => api.post('/api/equipamentos/juntar', { ids: equipamentos.map((e) => e.id), resultado }),
      (r) => `${plural(equipamentos.length, 'equipamento', 'equipamentos')} juntos em "${resultado.descricao}"${r.itens ? ` (${plural(r.itens, 'item atualizado', 'itens atualizados')})` : ''}.`
    ),
    guardarCabine: (original, nova) => executar(
      () => (original ? api.put(`/api/cabines/${original.id}`, nova) : api.post('/api/cabines', nova)),
      original ? 'Cabine atualizada.' : 'Cabine criada.'
    ),
    apagarCabine: (original) => executar(() => api.del(`/api/cabines/${original.id}`), 'Cabine apagada.'),
    guardarPessoa: async (original, nova) => {
      const ok = await executar(
        () => (original ? api.put(`/api/pessoas/${original.id}`, nova) : api.post('/api/pessoas', nova)),
        original ? 'Pessoa atualizada.' : 'Pessoa criada.'
      );
      if (ok && original && original.id === utilizador.id) await carregarSessao(); // mudou o próprio nome ou perfil
      return ok;
    },
    adicionarNaLista: (lista, valor) => executar(() => api.post('/api/listas', { lista, valor })),
    removerDaLista: (lista, valor) => executar(() => api.del('/api/listas', { lista, valor })),
  };

  async function sair() {
    try { await api.post('/api/logout'); } catch (e) { /* sai na mesma */ }
    setDados(null);
    setMe(null);
  }

  const abertos = itens.filter((a) => a.aberto);
  const contagens = {
    mes: abertos.filter((a) => !a.pendenteInstalacao).length,
    pendentes: abertos.filter((a) => a.pendenteInstalacao).length,
    tarefas: abertos.reduce((n, a) => n
      + a.abertas.filter((e) => admin || e.responsavel === utilizador.nome).length
      + a.etapas.flatMap((e) => e.subtarefas || []).filter((s) => !s.concluida && (admin || s.responsavel === utilizador.nome)).length, 0),
    base: dados.itens.length,
  };

  let ecra;
  if (local.rota === 'novo') ecra = <ScreenItemForm key="novo" />;
  else if (local.rota === 'editar') ecra = <ScreenItemForm key={`editar-${local.item}`} numeroEditar={local.item} />;
  else if (local.rota === 'pessoa') ecra = <ScreenPessoa key={`pessoa-${local.id || 'nova'}`} idEditar={local.id} />;
  else if (local.rota === 'base') ecra = <ScreenBase seccao={local.sec} />;
  else {
    const rota = ROTAS.find((r) => r.chave === local.rota) || ROTAS[0];
    ecra = <rota.Ecra />;
  }
  const mostrarDetalhe = local.item != null && local.rota !== 'editar' && dados.itens.some((i) => i.numero === local.item);
  const contexto = { dados, itens, utilizador, acoes, abrirItem, irPara, pesquisa };

  return (
    <ContextoLista.Provider value={contexto}>
      <Cabecalho local={local} contagens={contagens} pesquisa={pesquisa} setPesquisa={setPesquisa} utilizador={utilizador} onSair={sair} />
      <main className="mx-auto max-w-7xl px-4 py-8 md:px-6">
        {ecra}
      </main>
      <footer className="mx-auto max-w-7xl px-4 pb-10 text-xs text-muted-foreground md:px-6">
        Todos veem a mesma lista; atualiza sozinha a cada minuto. Cada alteração fica registada com o nome de quem a fez.
      </footer>

      {mostrarDetalhe && (
        <DetalheItem
          numero={local.item}
          onFechar={() => irPara(ROTAS_SEM_MENU.includes(local.rota) ? 'resumo' : local.rota, null, local.sec ? { sec: local.sec } : null)}
        />
      )}
      <Toast toast={toast} onFechar={() => setToast(null)} />
    </ContextoLista.Provider>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
