<h1>Photos</h1>
<button id="dropzone" type="button">drop shelf photos here, or click to choose</button>
<input id="filepick" type="file" accept=".jpg,.jpeg,.png,.heic" multiple hidden
       aria-label="choose shelf photos">

<h2 id="reshoot">Reshoot <span class="count">— boxes seen but not identified; nothing blocks on these</span></h2>
<div id="tickets"><p class="empty">No open reshoot tickets.</p></div>

<h2 id="gallery">Shelf photos <span class="count" id="gallerycount"></span></h2>
<div class="gallery" id="shots"></div>
<script>
"use strict";

function render(state, photos) {
  const tickets = document.getElementById("tickets");
  tickets.innerHTML = state.unidentified.length
    ? state.unidentified.map(s => ticketCard(s)).join("")
    : `<p class="empty">No open reshoot tickets.</p>`;
  wireDismiss(tickets, () => refresh().catch(() => {}));

  document.getElementById("gallerycount").textContent = `— ${photos.length} on file`;
  document.getElementById("shots").innerHTML = photos.map(p => `
    <figure class="shot">
      <a href="/photos/view/${encodeURIComponent(p.name)}" aria-label="open ${esc(p.name)} details">
        <img src="/photos/${encodeURIComponent(p.name)}" alt="shelf photo ${esc(p.name)}" loading="lazy"></a>
      <figcaption class="meta">${esc(p.name)}<br>
        ${p.extracted
          ? `<b>${p.titles}</b> title(s)${p.unidentified ? ` · ${p.unidentified} unidentified` : ""}`
          : `not extracted yet — run <b>extract</b>`}
      </figcaption>
    </figure>`).join("");
}

const GATE = changeGate();  // rebuilding innerHTML re-renders every <img>
async function refresh() {
  const [state, photos] = await Promise.all([
    fetchJSON("/api/state"),
    fetchJSON("/api/photos-list"),
  ]);
  GATE([state.unidentified, state.warnings, photos], () => render(state, photos));
}

const zone = document.getElementById("dropzone");
const pick = document.getElementById("filepick");
zone.addEventListener("click", () => pick.click());
zone.addEventListener("dragover", e => { e.preventDefault(); zone.classList.add("hot"); });
zone.addEventListener("dragleave", () => zone.classList.remove("hot"));
zone.addEventListener("drop", e => {
  e.preventDefault(); zone.classList.remove("hot");
  sendPhotos(e.dataTransfer.files);
});
pick.addEventListener("change", () => sendPhotos(pick.files));

const ZONE_IDLE = "drop shelf photos here, or click to choose";
let UPLOADING = false;

async function sendPhotos(files) {
  if (!files.length || UPLOADING) return;
  const form = new FormData();
  for (const f of files) form.append("files", f);
  const mb = ([...files].reduce((total, f) => total + f.size, 0) / 1048576).toFixed(1);
  UPLOADING = true;
  zone.disabled = true;
  zone.setAttribute("aria-busy", "true");
  zone.classList.remove("ok");
  zone.textContent = `uploading ${files.length} photo(s) — ${mb} MB… keep this page open`;
  let res = null;
  try {
    res = await fetch("/api/photos", {method: "POST", body: form});
  } catch (err) {
    alert("Upload failed (no response from the server): " + err);
  }
  UPLOADING = false;
  zone.disabled = false;
  zone.removeAttribute("aria-busy");
  pick.value = "";  // re-picking the same photo must fire change again
  if (!res) { zone.textContent = ZONE_IDLE; return; }
  if (!res.ok) {
    const detail = await res.json().then(d => d.detail).catch(() => null);
    zone.textContent = ZONE_IDLE;
    alert("Upload failed: " + (detail ?? res.statusText));
    return;
  }
  const saved = await res.json().then(d => d.saved || []).catch(() => []);
  zone.classList.add("ok");
  zone.textContent = `✓ saved ${saved.join(", ")} — add another?`;
  setTimeout(() => {
    if (!UPLOADING && zone.classList.contains("ok")) {
      zone.classList.remove("ok");
      zone.textContent = ZONE_IDLE;
    }
  }, 6000);
  GATE.reset();  // the new photo must appear even if nothing else changed
  refresh().catch(() => {});  // the next poll self-heals a refresh hiccup
}

// leaving mid-upload silently loses the photo — make the browser ask
window.addEventListener("beforeunload", e => {
  if (UPLOADING) e.preventDefault();
});

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