/**
 * <EnvioFicheiros> — largar vários ficheiros de uma vez.
 *
 * Depois do ensaio de 24 Set: quatro ficheiros carregados um a um, em dois
 * sítios, com uma caixa diferente em cada, e sem se saber onde tinham ficado
 * as imagens. O parecer do Codex simplificou: **guarda-se logo**, sem
 * "Está certo, guardar" -- largar já quer dizer guardar.
 *
 * O que chegou vê-se na lista de ficheiros, marcado "Novo" (quem usa isto
 * recebe cada um em `onGuardado`). Aqui fica só a frase do lote ("Chegaram 4:
 * 1 manual, 2 imagens, 1 já existia") e os que não entraram, com "Tentar outra
 * vez" quando vale a pena. Até à segunda ronda de 24 Set havia um recibo com
 * uma linha por ficheiro, guardado no browser: repetia a lista, misturava lotes
 * e ficava para trás quando se mudava um ficheiro na lista (cliente simulado e
 * revisor da simplicidade).
 *
 * Cada ficheiro vai no seu pedido (a API recebe um de cada vez), dois de cada
 * vez. As imagens não se lêem por omissão; ler o texto de um cartaz é um gesto
 * na lista. Numa campanha, o ficheiro fica dessa campanha.
 */

const PAPEIS_FICHEIRO = [
  ['logotipo', 'Logótipo'],
  ['manual', 'Manual de marca'],
  ['imagem', 'Imagem'],
  ['documento', 'Documento'],
  ['outro', 'Outro'],
];
const NOME_PAPEL_FICHEIRO = Object.fromEntries(PAPEIS_FICHEIRO);
const EH_IMAGEM_FICHEIRO = (f) => /^image\/(png|jpeg|webp)$/.test(f.type) || /\.(png|jpe?g|webp)$/i.test(f.name);

/** "Chegaram 4: 1 manual, 1 logótipo, 1 imagem, 1 já existia." — o lote, numa frase. */
function resumoDoLote(itens) {
  const plural = (q, um, varios) => `${q} ${q === 1 ? um : varios}`;
  const guardados = itens.filter((i) => i.estado === 'guardado');
  const quantos = (p) => guardados.filter((i) => i.papel === p).length;
  const partes = [];
  if (quantos('manual')) partes.push(plural(quantos('manual'), 'manual', 'manuais'));
  if (quantos('logotipo')) partes.push(plural(quantos('logotipo'), 'logótipo', 'logótipos'));
  if (quantos('imagem')) partes.push(plural(quantos('imagem'), 'imagem', 'imagens'));
  if (quantos('documento')) partes.push(plural(quantos('documento'), 'documento', 'documentos'));
  if (quantos('outro')) partes.push(plural(quantos('outro'), 'outro', 'outros'));
  const existia = itens.filter((i) => i.estado === 'existia').length;
  if (existia) partes.push(`${existia} já ${existia === 1 ? 'existia' : 'existiam'}`);
  const aEnviar = itens.filter((i) => i.estado === 'fila' || i.estado === 'a enviar').length;
  if (aEnviar) partes.push(`${aEnviar} a enviar`);
  const falhou = itens.filter((i) => i.estado === 'falhou').length;
  if (falhou) partes.push(`${falhou} não ${falhou === 1 ? 'entrou' : 'entraram'}`);
  return `${itens.length === 1 ? 'Chegou 1' : `Chegaram ${itens.length}`}: ${partes.join(', ')}.`;
}

async function enviarUmFicheiro(slug, ficheiro, campanhaId) {
  const q = new URLSearchParams({ nome: ficheiro.name, ler: EH_IMAGEM_FICHEIRO(ficheiro) ? '0' : '1' });
  if (campanhaId) q.set('campanha', campanhaId);
  let r;
  try {
    r = await fetch(`/api/${slug}/materiais?${q}`, {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': ficheiro.type || 'application/octet-stream' },
      body: ficheiro,
    });
  } catch {
    // "Failed to fetch" não diz nada a ninguém (cliente simulado, 24 Set).
    throw Object.assign(new Error('Sem ligação ao servidor. Tenta outra vez.'), { definitivo: false });
  }
  const corpo = await r.json().catch(() => null);
  if (!r.ok) {
    // Uma recusa (formato, tamanho, espaço) repete-se igual: não se oferece
    // "Tentar outra vez". Uma falha do servidor ou da rede, sim.
    throw Object.assign(new Error((corpo && corpo.error) || `Erro ${r.status}`), { definitivo: r.status >= 400 && r.status < 500 });
  }
  return corpo;
}

function EnvioFicheiros({ slug, campanhaId = null, podeMexer = true, dica, onGuardado }) {
  const [itens, setItens] = React.useState([]);
  const itensRef = React.useRef(itens);
  itensRef.current = itens;
  const campo = React.useRef(null);
  const aCorrer = React.useRef(0);

  const mudar = (chave, mudanca) => {
    itensRef.current = itensRef.current.map((i) => (i.chave === chave ? { ...i, ...mudanca } : i));
    setItens((lista) => lista.map((i) => (i.chave === chave ? { ...i, ...mudanca } : i)));
  };

  // Dois de cada vez: o servidor trata um envio de cada vez por conta, e o
  // segundo já está a subir enquanto o primeiro grava.
  const avancar = React.useCallback(() => {
    while (aCorrer.current < 2) {
      const proximo = itensRef.current.find((i) => i.estado === 'fila');
      if (!proximo) return;
      aCorrer.current += 1;
      mudar(proximo.chave, { estado: 'a enviar' });
      enviarUmFicheiro(slug, proximo.ficheiro, campanhaId)
        .then((r) => {
          mudar(proximo.chave, { estado: r.existia ? 'existia' : 'guardado', papel: r.material.papel, erro: null });
          if (onGuardado) onGuardado(r);
        })
        .catch((e) => mudar(proximo.chave, { estado: 'falhou', erro: e.message, definitivo: Boolean(e.definitivo) }))
        .finally(() => {
          aCorrer.current -= 1;
          setTimeout(avancar, 0);
        });
    }
  }, [slug, campanhaId, onGuardado]);

  // Um lote novo começa a frase de novo: não se somam lotes de antes.
  const receber = (lista) => {
    const aMeio = itensRef.current.filter((i) => i.estado === 'fila' || i.estado === 'a enviar');
    const novos = [...(lista || [])].map((f) => ({
      chave: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
      ficheiro: f,
      nome: f.name,
      estado: 'fila',
    }));
    if (!novos.length) return;
    itensRef.current = [...aMeio, ...novos];
    setItens(itensRef.current);
    if (campo.current) campo.current.value = '';
    setTimeout(avancar, 0);
  };

  const repetir = (item) => {
    mudar(item.chave, { estado: 'fila', erro: null });
    setTimeout(avancar, 0);
  };

  const aEnviar = itens.filter((i) => i.estado === 'a enviar' || i.estado === 'fila').length;
  const falhados = itens.filter((i) => i.estado === 'falhou');

  return (
    <div className="space-y-2">
      {podeMexer && (
        <ZonaFicheiro
          id={`envio-${campanhaId || 'marca'}`}
          campoRef={campo}
          multiplo
          aEnviar={aEnviar ? `${aEnviar} ${aEnviar === 1 ? 'ficheiro' : 'ficheiros'}` : null}
          onFicheiros={receber}
          dica={dica}
        />
      )}
      {itens.length > 0 && (
        <div className="flex flex-wrap items-baseline justify-between gap-2">
          <p className="text-sm text-foreground" role="status">{resumoDoLote(itens)}</p>
          {!aEnviar && (
            <button type="button" onClick={() => setItens([])} className="text-xs text-muted-foreground hover:text-foreground">
              Fechar
            </button>
          )}
        </div>
      )}
      {falhados.length > 0 && (
        <ul className="space-y-1">
          {falhados.map((i) => (
            <li key={i.chave} className="flex flex-wrap items-baseline gap-x-3 text-xs">
              <span className="text-foreground">{i.nome}</span>
              <span className="text-accent">{i.erro}</span>
              {!i.definitivo && podeMexer && (
                <button type="button" onClick={() => repetir(i)} className="text-foreground underline underline-offset-2 hover:text-primary">
                  Tentar outra vez
                </button>
              )}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}
