// Cmd+K / Ctrl+K command palette. Hits /api/search and renders editorial-card
// results. Vanilla JS, no deps; styles come from output.css (Tailwind).
(function () {
  const overlay = document.getElementById("pete-search");
  if (!overlay) return;
  const input = overlay.querySelector("[data-search-input]");
  const list = overlay.querySelector("[data-search-list]");
  const meta = overlay.querySelector("[data-search-meta]");

  let activeIndex = -1;
  let items = [];
  let inflight = null;
  let debounceTimer = 0;
  let lastQuery = "";

  function open() {
    if (!overlay.classList.contains("hidden")) return;
    overlay.classList.remove("hidden");
    document.body.classList.add("overflow-hidden");
    requestAnimationFrame(() => input.focus());
  }

  function close() {
    overlay.classList.add("hidden");
    document.body.classList.remove("overflow-hidden");
    input.value = "";
    list.innerHTML = "";
    meta.textContent = "";
    items = [];
    activeIndex = -1;
    lastQuery = "";
  }

  function escapeHTML(s) {
    return String(s == null ? "" : s)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#39;");
  }

  // safeURL rejects any href whose scheme isn't http(s). Stored article_url
  // comes from feeds we don't control; a hostile feed publishing
  // `javascript:fetch(...)` would otherwise execute on click.
  function safeURL(s) {
    const raw = String(s == null ? "" : s).trim();
    if (/^https?:\/\//i.test(raw)) return raw;
    return "";
  }

  function render(results) {
    items = results || [];
    activeIndex = items.length > 0 ? 0 : -1;
    if (items.length === 0) {
      list.innerHTML = "";
      return;
    }
    const html = items.map((r, i) => {
      const thumb = r.thumb_url
        ? `<div class="hidden sm:block w-20 h-20 shrink-0 overflow-hidden rounded-2xl bg-[color:var(--ink)]/5">
             <img src="${escapeHTML(r.thumb_url)}" alt="${escapeHTML(r.headline || "")}" loading="lazy" decoding="async"
                  class="h-full w-full object-cover">
           </div>`
        : `<div class="hidden sm:flex w-20 h-20 shrink-0 items-center justify-center rounded-2xl bg-[color:var(--ink)]/5 text-2xl">${escapeHTML(r.channel_emoji || "·")}</div>`;
      const postedRing = r.posted ? ` border-theme-${escapeHTML(r.channel_theme)}` : "";
      const href = safeURL(r.article_url);
      if (!href) return "";
      return `
        <a href="${escapeHTML(href)}" target="_blank" rel="noopener noreferrer"
           data-idx="${i}"
           class="search-result group flex items-start gap-4 rounded-2xl p-3 transition border-2 border-transparent hover:bg-[color:var(--ink)]/5${postedRing}">
          ${thumb}
          <div class="min-w-0 flex-1 space-y-1">
            <div class="flex items-center gap-2 text-xs flex-wrap">
              <span class="inline-flex items-center rounded-full bg-theme-${escapeHTML(r.channel_theme)} text-white px-2.5 py-0.5 font-semibold uppercase tracking-wider">
                <span aria-hidden="true" class="mr-1">${escapeHTML(r.channel_emoji)}</span>${escapeHTML(r.channel_title)}
              </span>
              <span class="text-[color:var(--ink)]/60 font-semibold">${escapeHTML(r.source)}</span>
              <span class="text-[color:var(--ink)]/50">${escapeHTML(r.time_ago)}</span>
            </div>
            <h3 class="font-display text-base sm:text-lg font-semibold leading-snug">${escapeHTML(r.headline)}</h3>
            ${r.lede ? `<p class="text-sm text-[color:var(--ink)]/70 line-clamp-2">${escapeHTML(r.lede)}</p>` : ""}
          </div>
        </a>`;
    }).join("");
    list.innerHTML = html;
    paintActive();
  }

  function paintActive() {
    const nodes = list.querySelectorAll(".search-result");
    nodes.forEach((n, i) => {
      if (i === activeIndex) {
        n.classList.add("bg-[color:var(--ink)]/5", "border-[color:var(--ink)]/15");
        n.scrollIntoView({ block: "nearest" });
      } else {
        n.classList.remove("bg-[color:var(--ink)]/5", "border-[color:var(--ink)]/15");
      }
    });
  }

  async function runQuery(q) {
    if (q === lastQuery) return;
    lastQuery = q;
    if (!q) {
      render([]);
      meta.textContent = "type to search · esc to close";
      return;
    }
    if (inflight) inflight.abort();
    const ctrl = new AbortController();
    inflight = ctrl;
    meta.textContent = "searching…";
    try {
      const res = await fetch("/api/search?q=" + encodeURIComponent(q), { signal: ctrl.signal });
      if (!res.ok) throw new Error("search failed: " + res.status);
      const data = await res.json();
      if (ctrl !== inflight) return;
      render(data.results || []);
      const n = (data.results || []).length;
      meta.textContent = n === 0 ? "no matches" : `${n} result${n === 1 ? "" : "s"}`;
    } catch (err) {
      if (err.name === "AbortError") return;
      console.error(err);
      meta.textContent = "search error";
    } finally {
      if (ctrl === inflight) inflight = null;
    }
  }

  input.addEventListener("input", () => {
    clearTimeout(debounceTimer);
    const q = input.value.trim();
    debounceTimer = window.setTimeout(() => runQuery(q), 120);
  });

  input.addEventListener("keydown", (e) => {
    if (e.key === "ArrowDown") {
      e.preventDefault();
      if (items.length === 0) return;
      activeIndex = (activeIndex + 1) % items.length;
      paintActive();
    } else if (e.key === "ArrowUp") {
      e.preventDefault();
      if (items.length === 0) return;
      activeIndex = (activeIndex - 1 + items.length) % items.length;
      paintActive();
    } else if (e.key === "Enter") {
      if (activeIndex >= 0 && items[activeIndex]) {
        e.preventDefault();
        const href = safeURL(items[activeIndex].article_url);
        if (href) window.open(href, "_blank", "noopener");
      }
    }
  });

  overlay.addEventListener("click", (e) => {
    if (e.target === overlay) close();
  });

  document.addEventListener("keydown", (e) => {
    const isK = e.key === "k" || e.key === "K";
    if ((e.metaKey || e.ctrlKey) && isK) {
      e.preventDefault();
      if (overlay.classList.contains("hidden")) open();
      else close();
      return;
    }
    if (e.key === "Escape" && !overlay.classList.contains("hidden")) {
      close();
    }
    if (e.key === "/" && overlay.classList.contains("hidden")) {
      const tag = (document.activeElement && document.activeElement.tagName) || "";
      if (tag === "INPUT" || tag === "TEXTAREA") return;
      e.preventDefault();
      open();
    }
  });

  document.querySelectorAll("[data-search-trigger]").forEach((btn) => {
    btn.addEventListener("click", (e) => {
      e.preventDefault();
      open();
    });
  });

  meta.textContent = "type to search · esc to close";
})();
