// dubUndo.js
// Scope: DUBS panel quick undo menu for recent project-scoped dubs.

(() => {
  const MAX_UNDO_DUBS = 10;
  let panel = null;
  let toggle = null;

  function q(id) { return document.getElementById(id); }

  async function api(path, options = {}) {
    const res = await fetch(path, {
      cache: "no-store",
      ...options,
      headers: { "Content-Type": "application/json", ...(options.headers || {}) }
    });
    const data = await res.json().catch(() => ({}));
    if (!res.ok || data?.ok === false) throw new Error(data?.message || data?.error || data?.reason || `request_failed:${res.status}`);
    return data;
  }

  function escapeHtml(value) {
    return String(value ?? "").replace(/[&<>"]/g, ch => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", "\"": "&quot;" }[ch]));
  }

  function shortTime(ts) {
    const n = Number(ts || 0);
    if (!n) return "";
    try { return new Date(n).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); }
    catch { return ""; }
  }

  function itemHtml(item) {
    const diff = Array.isArray(item.diffLines) ? item.diffLines : [];
    return `<div class="dub-undo-item" data-dub-id="${escapeHtml(item.dubID)}">
      <div class="dub-undo-card-main">
        <div class="dub-undo-card-top"><span class="dub-undo-arrow">↶</span><span class="dub-undo-kind">${escapeHtml(item.action || "RUN")}</span><span class="dub-undo-file">${escapeHtml(item.filePath || "")}</span><span class="dub-undo-time">${escapeHtml(shortTime(item.timestamp))}</span></div>
        <div class="dub-undo-task">${escapeHtml(item.task || item.dubID || "dub")}</div>
        <pre class="dub-undo-diff">${escapeHtml(diff.join("\n") || "diff located")}</pre>
      </div>
      <button class="dub-undo-item-btn" type="button" data-dub-id="${escapeHtml(item.dubID)}">UNDO</button>
    </div>`;
  }

  function setPanelContent(html) {
    if (!panel) return;
    panel.innerHTML = html;
    panel.querySelectorAll(".dub-undo-item-btn").forEach(btn => {
      btn.addEventListener("click", () => applyDubUndo(btn.dataset.dubId));
    });
  }

  async function loadUndoList() {
    setPanelContent(`<div class="dub-undo-note">Loading recent undo candidates…</div>`);
    try {
      const data = await api(`/recovery/undo/recent?limit=${MAX_UNDO_DUBS}`);
      const items = Array.isArray(data.items) ? data.items : [];
      setPanelContent(`<div class="dub-undo-note">Undo reverses a recent dub session via DIFF log. Larger restorations may require restoring from scrapes or backups.</div>
        <div class="dub-undo-items">${items.length ? items.map(itemHtml).join("") : `<div class="dub-undo-empty">No eligible recent dubs found.</div>`}</div>`);
    } catch (err) {
      setPanelContent(`<div class="dub-undo-empty">${escapeHtml(err?.message || String(err))}</div>`);
    }
  }

  async function applyDubUndo(dubID) {
    if (!dubID) return;
    const ok = window.confirm("Would you like to undo this change?\n\nThis cannot be reversed, but it can be processed again.");
    if (!ok) return;
    try {
      await api("/recovery/undo/dub", { method: "POST", body: JSON.stringify({ dubID }) });
      if (typeof window.showToast === "function") window.showToast("Undo submitted.");
      await loadUndoList();
      if (typeof window.loadDubs === "function") window.loadDubs();
    } catch (err) {
      alert(`Undo failed: ${err?.message || String(err)}`);
    }
  }

  function closePanel() {
    if (!panel || !toggle) return;
    panel.hidden = true;
    toggle.classList.remove("active");
  }

  async function togglePanel() {
    if (!panel || !toggle) return;
    const opening = panel.hidden;
    panel.hidden = !opening;
    toggle.classList.toggle("active", opening);
    if (opening) await loadUndoList();
  }

  const DUB_CLEAR_STORE_KEY = "verbatim.dubs.clearedDisplayed.v1";

  function readDisplayedClearSet() {
    try {
      return new Set(JSON.parse(sessionStorage.getItem(DUB_CLEAR_STORE_KEY) || "[]"));
    } catch {
      return new Set();
    }
  }

  function writeDisplayedClearSet(values) {
    try {
      sessionStorage.setItem(DUB_CLEAR_STORE_KEY, JSON.stringify([...values].slice(-500)));
    } catch {}
  }

  function dubItemIdentity(item, index = 0) {
    return String(
      item?.dataset?.dubId ||
      item?.dataset?.id ||
      item?.querySelector?.(".dub-id")?.textContent ||
      item?.querySelector?.(".dub-action")?.textContent ||
      item?.textContent ||
      `dub-${index}`
    ).trim();
  }

  function isDisplayedDubItem(item) {
    if (!item || item.hidden || item.classList.contains("dub-cleared-displayed")) return false;
    const style = window.getComputedStyle(item);
    return style.display !== "none" && style.visibility !== "hidden" && item.getClientRects().length > 0;
  }

  function syncDisplayedDubCounts() {
    const items = Array.from(document.querySelectorAll("#dubs-list .dub-item")).filter(isDisplayedDubItem);
    const count = id => document.getElementById(id);
    const byKind = kind => items.filter(item => item.classList.contains(kind)).length;

    const total = count("dubs-count");
    if (total) total.textContent = String(items.length);

    const error = count("dubs-count-error");
    const run = count("dubs-count-run");
    const diagnostic = count("dubs-count-diagnostic");
    const enhancement = count("dubs-count-enhancement");

    if (error) error.textContent = String(byKind("error"));
    if (run) run.textContent = String(byKind("run"));
    if (diagnostic) diagnostic.textContent = String(byKind("diagnostic"));
    if (enhancement) enhancement.textContent = String(byKind("enhancement"));

    const empty = document.getElementById("dubs-empty");
    if (empty) empty.style.display = items.length ? "none" : "block";
  }

  function applyDisplayedDubClearFilter() {
    const cleared = readDisplayedClearSet();
    Array.from(document.querySelectorAll("#dubs-list .dub-item")).forEach((item, index) => {
      if (!cleared.has(dubItemIdentity(item, index))) return;
      item.classList.add("dub-cleared-displayed");
      item.hidden = true;
      item.style.display = "none";
    });
    syncDisplayedDubCounts();
  }

  function clearDisplayedDubs(event) {
    event.preventDefault();
    event.stopImmediatePropagation();

    const displayed = Array.from(document.querySelectorAll("#dubs-list .dub-item")).filter(isDisplayedDubItem);
    if (!displayed.length) {
      if (typeof window.showToast === "function") window.showToast("No displayed dubs to clear.");
      return;
    }

    const cleared = readDisplayedClearSet();
    displayed.forEach((item, index) => {
      cleared.add(dubItemIdentity(item, index));
      item.classList.add("dub-cleared-displayed");
      item.hidden = true;
      item.style.display = "none";
    });
    writeDisplayedClearSet(cleared);
    syncDisplayedDubCounts();

    if (typeof window.showToast === "function") window.showToast(`Cleared ${displayed.length} displayed dub${displayed.length === 1 ? "" : "s"}.`);
  }

  function installDisplayedDubClear() {
    const clearBtn = q("dubs-clear-btn");
    const list = q("dubs-list");
    if (!clearBtn || clearBtn.dataset.clearDisplayedBound === "true") return;

    clearBtn.textContent = "CLEAR DISPLAYED";
    clearBtn.title = "Clear only the DUBS items currently displayed in this view";
    clearBtn.dataset.clearDisplayedBound = "true";
    clearBtn.addEventListener("click", clearDisplayedDubs, true);

    if (list) {
      const observer = new MutationObserver(() => applyDisplayedDubClearFilter());
      observer.observe(list, { childList: true, subtree: false });
      applyDisplayedDubClearFilter();
    }

    if (typeof window.loadDubs === "function" && !window.loadDubs.__clearDisplayedWrapped) {
      const originalLoadDubs = window.loadDubs;
      window.loadDubs = async function(...args) {
        const result = await originalLoadDubs.apply(this, args);
        setTimeout(applyDisplayedDubClearFilter, 0);
        return result;
      };
      window.loadDubs.__clearDisplayedWrapped = true;
    }
  }

  function install() {
    const head = q("dubs-review-head");
    const dubsPanel = q("panel-dubs");
    if (!head || !dubsPanel || q("dubs-undo-toggle")) return;

    installDisplayedDubClear();

    const clearBtn = q("dubs-clear-btn");
    toggle = document.createElement("button");
    toggle.id = "dubs-undo-toggle";
    toggle.className = "inp-btn dubs-undo-toggle";
    toggle.type = "button";
    toggle.textContent = "Undo";
    toggle.addEventListener("click", togglePanel);
    if (clearBtn) clearBtn.insertAdjacentElement("afterend", toggle);
    else head.appendChild(toggle);

    panel = document.createElement("div");
    panel.id = "dubs-undo-panel";
    panel.hidden = true;
    panel.setAttribute("aria-label", "Recent dub undo menu");
    dubsPanel.appendChild(panel);

    document.addEventListener("click", event => {
      if (!panel || panel.hidden) return;
      if (panel.contains(event.target) || toggle.contains(event.target)) return;
      closePanel();
    });
  }

  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", install);
  else install();
})();
