// configuration's list + inspector: one entry shows at a time, picked from the list, the
// prev/next buttons, ↑↓ on the list, or a #hash. the address bar's path grows with the
// selection: configuration ▸ firehose ▸ FIREHOSE_WORKERS, and each ▸ lists that level.

import { tree } from "./tree";

const pane = document.querySelector<HTMLElement>("[data-cfg-pane]");
if (pane) setup(pane);

function setup(pane: HTMLElement) {
  const list = pane.querySelector<HTMLElement>("[data-tree]")!;
  const outline = tree(list);
  const items = [...pane.querySelectorAll<HTMLElement>(".cfg-item")];
  const keys = items.map((i) => i.dataset.item!);
  const inav = pane.querySelector<HTMLElement>("[data-inav]")!;
  const paper = pane.querySelector<HTMLElement>(".paper")!;
  let cur = -1;

  const sectionOf = (i: number) => {
    const it = items[i];
    return it.classList.contains("cfg-var") ? it.dataset.sec! : it.classList.contains("cfg-sec") ? it.dataset.item! : null;
  };
  const nameOf = (i: number) => keys[i] === "" ? list.querySelector(".side-title .k")!.textContent! : items[i].classList.contains("cfg-sec") ? items[i].querySelector("h2")!.textContent! : keys[i];

  function select(i: number, how: "push" | "replace" | "none" = "none") {
    if (i < 0 || i >= items.length) return;
    cur = i;
    items.forEach((it, j) => it.classList.toggle("cur", j === i));
    outline.select(keys[i]);
    // a new entry starts at its top, like turning to a page
    if (window.scrollY > 0 && !matchMedia("(max-width: 760px)").matches) window.scrollTo({ top: 0 });

    const [prev, next] = [inav.querySelector<HTMLAnchorElement>('[data-step="-1"]')!, inav.querySelector<HTMLAnchorElement>('[data-step="1"]')!];
    for (const [a, j, arrow] of [[prev, i - 1, "◀ "], [next, i + 1, " ▶"]] as const) {
      const ok = j >= 0 && j < items.length;
      a.setAttribute("aria-disabled", String(!ok));
      a.href = ok ? `#${keys[j]}` : "#";
      a.textContent = ok ? (arrow === "◀ " ? arrow + nameOf(j) : nameOf(j) + arrow) : arrow.trim();
    }
    inav.hidden = false;

    const hash = keys[i] ? `#${keys[i]}` : location.pathname;
    if (how === "push") history.pushState(null, "", hash);
    if (how === "replace") history.replaceState(null, "", hash);
    crumbs(i);
  }

  const fromHash = () => Math.max(0, keys.indexOf(decodeURIComponent(location.hash.slice(1))));

  // the address bar ends at "configuration" in the html; past that the path follows the selection
  const addr = document.querySelector<HTMLElement>("[data-addr]");
  const trail = addr?.querySelector<HTMLElement>(".crumbs");
  const here = trail?.querySelector<HTMLElement>(".crumb.current");
  const pageLabel = here?.textContent ?? "";
  function crumbs(i: number) {
    if (!addr || !trail || !here) return;
    trail.querySelectorAll("[data-cfg-crumb]").forEach((n) => n.remove());
    addr.querySelectorAll("[data-cfg-menu]").forEach((n) => n.remove());
    const sec = sectionOf(i);
    here.hidden = !!sec;
    here.classList.toggle("current", !sec);
    if (!sec) return;
    const si = keys.indexOf(sec);
    const isVar = items[i].classList.contains("cfg-var");
    const sep = (id: string, label: string, entries: number[]) => {
      const m = document.createElement("div");
      m.className = "menu anchored";
      m.popover = "manual";
      m.style.setProperty("position-anchor", `--sib-${id}`);
      m.dataset.sibmenu = id;
      m.dataset.cfgMenu = "";
      m.innerHTML = entries.map((j) => `<a href="#${keys[j]}"${j === i || (j === si && !isVar) ? ' aria-current="page"' : ""}><span class="lbl">${nameOf(j)}</span></a>`).join("");
      addr.append(m);
      return `<button class="crumb-sep" type="button" aria-expanded="false" aria-label="${label}" data-sib="${id}" style="anchor-name: --sib-${id}" data-cfg-crumb></button>`;
    };
    const sectionIdx = keys.map((_, j) => j).filter((j) => items[j].classList.contains("cfg-sec"));
    const inSection = keys.map((_, j) => j).filter((j) => items[j].dataset.sec === sec);
    let html = `<a class="crumb" href="#" data-cfg-crumb>${pageLabel}</a>` + sep("cfg-secs", "sections", sectionIdx);
    html += isVar
      ? `<a class="crumb" href="#${sec}" data-cfg-crumb>${nameOf(si)}</a>` + sep("cfg-vars", `settings in ${nameOf(si)}`, inSection) + `<span class="crumb current" data-type data-cfg-crumb>${keys[i]}</span>`
      : `<span class="crumb current" data-type data-cfg-crumb>${nameOf(si)}</span>`;
    here.insertAdjacentHTML("afterend", html);
  }

  pane.addEventListener("click", (e) => {
    const a = (e.target as HTMLElement).closest<HTMLAnchorElement>("a[href^='#']");
    if (!a || a.getAttribute("aria-disabled") === "true") {
      if (a) e.preventDefault();
      return;
    }
    const j = keys.indexOf(decodeURIComponent(a.hash.slice(1)));
    if (j < 0 && a.getAttribute("href") !== "#") return;
    e.preventDefault();
    select(Math.max(0, j), "push");
    if (list.contains(a)) list.querySelector<HTMLElement>(".tree")!.focus({ preventScroll: true });
    if (matchMedia("(max-width: 760px)").matches) paper.scrollIntoView({ behavior: "smooth", block: "start" });
  });
  // the address bar's own crumbs and sibling menus link into the page too
  addr?.addEventListener("click", (e) => {
    const a = (e.target as HTMLElement).closest<HTMLAnchorElement>("a[href^='#']");
    if (!a || a.pathname !== location.pathname) return;
    const j = keys.indexOf(decodeURIComponent(a.hash.slice(1)));
    if (j < 0 && a.getAttribute("href") !== "#") return;
    e.preventDefault();
    select(Math.max(0, j), "push");
  });
  list.addEventListener("keydown", (e) => {
    if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
    e.preventDefault();
    const visible = outline.visible().map((k) => keys.indexOf(k));
    const at = visible.indexOf(cur);
    const next = visible[Math.max(0, Math.min(visible.length - 1, at + (e.key === "ArrowDown" ? 1 : -1)))];
    if (next !== undefined) select(next, "replace");
  });
  window.addEventListener("popstate", () => select(fromHash()));
  window.addEventListener("hashchange", () => select(fromHash()));

  select(fromHash());
}

export {};
