<h1>Upload queue</h1>
<p class="pagebar">Exactly what the upload stage will do, and what it has already done —
inspect here before any real run.</p>
<div id="queuebody"></div>
<script>
"use strict";

function table(headers, rows) {
  return `<div class="ledger"><table>
    <tr>${headers.map(h => `<th scope="col">${esc(h)}</th>`).join("")}</tr>
    ${rows.join("")}</table></div>`;
}

// a queue row's state comes from the upload log: the CSVs are diff-time
// snapshots and never shrink as work completes
function stateChip(r) {
  if (r.stale) return `<span class="chip no" title="${esc(r.stale)}">retired</span>`;
  if (r.state === "done" && r.last_status === "added_no_version")
    return `<span class="chip ok" title="the add saved without an edition — set it on BGG by hand if it matters">done · no version</span>`;
  if (r.state === "done") return `<span class="chip ok">done</span>`;
  if (r.state === "failed") return `<span class="chip no">failed</span>`;
  return `<span class="chip open">pending</span>`;
}

function tally(rows) {
  const n = s => rows.filter(r => !r.stale && r.state === s).length;
  const parts = [`<b>${n("")}</b> pending`];
  if (n("done")) parts.push(`<b>${n("done")}</b> done`);
  if (n("failed")) parts.push(`<b>${n("failed")}</b> failed`);
  const retired = rows.filter(r => r.stale).length;
  if (retired) parts.push(`<b>${retired}</b> retired by review`);
  return parts.join(" · ");
}

function render(q) {
  let html = "";

  html += `<h2>To add <span class="count">— ${tally(q.to_add)}</span></h2>`;
  html += q.to_add.length
    ? table(["game", "version", "seen in", ""], q.to_add.map(r => `
        <tr><td class="t">${esc(r.bgg_name)} <span class="meta">· ${esc(r.bgg_id)}</span></td>
        <td>${r.version_name ? esc(r.version_name) : `<span class="meta">no version</span>`}</td>
        <td class="meta">${esc((r.source_photos ?? "").split(";").join(", "))}</td>
        <td>${stateChip(r)}</td></tr>`))
    : `<p class="empty">Nothing queued — run <b>diff</b> from the <a href="/">pipeline</a> first.</p>`;

  html += `<h2>Version updates <span class="count">— ${tally(q.to_update)}</span></h2>`;
  html += q.to_update.length
    ? table(["game", "version to set", "collection id", ""], q.to_update.map(r => `
        <tr><td class="t">${esc(r.bgg_name)} <span class="meta">· ${esc(r.bgg_id)}</span></td>
        <td>${esc(r.version_name)}</td>
        <td class="meta">${esc(r.collid)}</td>
        <td>${stateChip(r)}</td></tr>`))
    : `<p class="empty">No version updates pending.</p>`;
  if (q.to_add.concat(q.to_update).some(r => r.state === "done"))
    html += `<p class="empty">Finished jobs stay listed until the next <b>diff</b>
      rebuilds the queue — the log below is the permanent record.</p>`;

  html += `<h2>Upload log <span class="count">— every attempt ever made (${q.log.length})</span></h2>`;
  html += q.log.length
    ? table(["when", "action", "game", "status", "note"], q.log.slice().reverse().map(r => `
        <tr><td class="meta">${esc(r.timestamp)}</td>
        <td class="meta">${esc(r.action)}</td>
        <td class="t">${esc(r.name)}</td>
        <td>${esc(r.status)}</td>
        <td class="meta">${esc(r.error)}</td></tr>`))
    : `<p class="empty">No uploads attempted yet.</p>`;

  document.getElementById("queuebody").innerHTML = html;
}

const GATE = changeGate();
async function refresh() {
  const q = await fetchJSON("/api/queue");
  GATE(q, () => render(q));
}

refresh().catch(err => errorBanner(err.message || err));
pollLoop(refresh, 5000, () => showBanner(""));
</script>
