<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 ticket(s) {
  const img = s.photo_exists
    ? `<a href="/photos/${encodeURIComponent(s.photo)}" target="_blank" tabindex="-1">
         <img src="/photos/${encodeURIComponent(s.photo)}" alt="photo ${esc(s.photo)}"></a>`
    : "";
  return `
  <section class="ticket"
           data-photo="${esc(s.photo)}" data-location="${esc(s.location)}"
           data-partial="${esc(s.partial_text)}" data-art="${esc(s.art_notes)}">
    <span class="stencil">reshoot</span>
    ${img}
    <div>
      <div class="loc">${esc(s.location) || "somewhere in " + esc(s.photo)}</div>
      ${s.partial_text ? `<div class="partial">text visible: ${esc(s.partial_text)}</div>` : ""}
      ${s.art_notes ? `<div class="notes">${esc(s.art_notes)}</div>` : ""}
      <div class="notes">from ${esc(s.photo)} — take a closer shot and drop it above</div>
      <button class="dismiss">dismiss — found it / not a game</button>
    </div>
  </section>`;
}

function render(state, photos) {
  const tickets = document.getElementById("tickets");
  tickets.innerHTML = state.unidentified.length
    ? state.unidentified.map(ticket).join("")
    : `<p class="empty">No open reshoot tickets.</p>`;
  tickets.querySelectorAll(".dismiss").forEach(b => b.addEventListener("click", async () => {
    const t = b.closest(".ticket");
    const res = await apiPost("/api/dismiss", {
      photo: t.dataset.photo, location: t.dataset.location,
      partial_text: t.dataset.partial, art_notes: t.dataset.art,
    });
    if (res) refresh();
  }));

  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("");
}

let LAST = null;
async function refresh() {
  const [state, photos] = await Promise.all([
    fetchJSON("/api/state"),
    fetchJSON("/api/photos-list"),
  ]);
  // re-render only on change: rebuilding innerHTML re-renders every <img>
  const payload = JSON.stringify([state.unidentified, state.warnings, photos]);
  if (payload === LAST) return;
  LAST = payload;
  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));

async function sendPhotos(files) {
  if (!files.length) return;
  const form = new FormData();
  for (const f of files) form.append("files", f);
  let res;
  try {
    res = await fetch("/api/photos", {method: "POST", body: form});
  } catch (err) {
    alert("Upload failed (no response from the server): " + err);
    return;
  }
  if (!res.ok) {
    const detail = await res.json().then(d => d.detail).catch(() => null);
    alert("Upload failed: " + (detail ?? res.statusText));
    return;
  }
  refresh().catch(() => {});  // the next poll self-heals a refresh hiccup
}

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