/**
 * sitemap-interactive.js
 * Drop into /assets/scripts/ — no .org changes required.
 *
 * Handles two page structures generated by org-publish:
 *   1. FLAT LIST  — a top-level <ul class="org-ul"> (e.g. Sitemap)
 *   2. OUTLINE    — <div class="outline-2/3"> with <h2>/<h3> headings and
 *                   nested <ul class="org-ul"> (e.g. "2025 List", "Blogs List")
 *
 * Activated on any page whose <h1 class="title"> matches SITEMAP_PATTERNS.
 */

(function () {
  "use strict";

  const SITEMAP_PATTERNS = ["sitemap", "list"];

  function init() {
    const titleEl = document.querySelector("h1.title");
    if (!titleEl) return;
    const title = titleEl.textContent.trim().toLowerCase();
    if (!SITEMAP_PATTERNS.some((p) => title.includes(p))) return;

    const contentDiv = document.getElementById("content");
    if (!contentDiv) return;

    const hasOutline = !!contentDiv.querySelector(".outline-2, .outline-3");
    const topUl = !hasOutline && contentDiv.querySelector("ul.org-ul");
    if (!hasOutline && !topUl) return;

    // ── Parsers ───────────────────────────────────────────────────────────

    function parseLi(li) {
      const link = li.querySelector(":scope > a");
      const childUl = li.querySelector(":scope > ul");
      const textNode = Array.from(li.childNodes).find(
        (n) => n.nodeType === Node.TEXT_NODE && n.textContent.trim()
      );
      const tags = Array.from(li.querySelectorAll(".post-tag")).map((t) =>
        t.textContent.trim()
      );
      const dateEl = li.querySelector(".post-date");
      const date = dateEl ? dateEl.textContent.trim() : null;

      return {
        label: link
          ? link.textContent.trim()
          : textNode
          ? textNode.textContent.trim()
          : li.childNodes[0]?.textContent?.trim() || "",
        href: link ? link.getAttribute("href") : null,
        tags,
        date,
        children: childUl ? parseUl(childUl) : [],
      };
    }

    function parseUl(ul) {
      return Array.from(ul.children)
        .filter((li) => li.tagName === "LI")
        .map(parseLi);
    }

    function parseFlatList() {
      return { tree: parseUl(topUl), replaceTarget: topUl, wrapOutline: false };
    }

    function parseOutline() {
      const tree = [];
      const outline2s = contentDiv.querySelectorAll(":scope .outline-2");

      if (outline2s.length === 0) {
        // Only outline-3 directly (flat month grouping without year wrapper)
        contentDiv.querySelectorAll(":scope .outline-3").forEach((section) => {
          const heading = section.querySelector("h3");
          const ul = section.querySelector("ul.org-ul");
          tree.push({
            label: heading ? heading.textContent.trim() : "Section",
            href: null, tags: [], date: null,
            children: ul ? parseUl(ul) : [],
          });
        });
      } else {
        outline2s.forEach((o2) => {
          const h2 = o2.querySelector(":scope > div > h2, :scope > h2");
          const groupNode = {
            label: h2 ? h2.textContent.trim() : "Group",
            href: null, tags: [], date: null,
            children: [],
          };
          const outline3s = o2.querySelectorAll(".outline-3");
          if (outline3s.length > 0) {
            outline3s.forEach((o3) => {
              const h3 = o3.querySelector(":scope > div > h3, :scope > h3");
              const ul = o3.querySelector("ul.org-ul");
              groupNode.children.push({
                label: h3 ? h3.textContent.trim() : "Month",
                href: null, tags: [], date: null,
                children: ul ? parseUl(ul) : [],
              });
            });
          } else {
            const ul = o2.querySelector("ul.org-ul");
            if (ul) groupNode.children = parseUl(ul);
          }
          tree.push(groupNode);
        });
      }

      return { tree, replaceTarget: null, wrapOutline: true };
    }

    const parsed = hasOutline ? parseOutline() : parseFlatList();
    const { tree } = parsed;
    if (!tree.length) return;

    // ── Styles ────────────────────────────────────────────────────────────
    if (!document.getElementById("sm-styles")) {
      const style = document.createElement("style");
      style.id = "sm-styles";
      style.textContent = `
        .sitemap-interactive { font-family: inherit; margin: 1.5rem 0; }
        .sm-toolbar {
          display: flex; align-items: center; gap: .6rem;
          margin-bottom: 1rem; flex-wrap: wrap;
        }
        .sm-search {
          flex: 1; min-width: 160px; padding: .35rem .7rem;
          border: 1px solid var(--border, #444); border-radius: 4px;
          background: var(--bg, transparent); color: inherit; font-size: .9rem;
        }
        .sm-search:focus { outline: 2px solid var(--accent, #7aa2f7); outline-offset: 1px; }
        .sm-expand-all, .sm-collapse-all {
          padding: .3rem .65rem; border: 1px solid var(--border, #444);
          border-radius: 4px; background: transparent; color: inherit;
          font-size: .8rem; cursor: pointer; opacity: .75; transition: opacity .15s;
        }
        .sm-expand-all:hover, .sm-collapse-all:hover { opacity: 1; }

        .sm-tree ul { list-style: none; margin: 0; padding: 0 0 0 1.4rem; }
        .sm-tree > ul { padding-left: 0; }
        .sm-tree li { margin: 0; }
        .sm-node {
          display: flex; align-items: center; gap: .4rem;
          padding: .2rem .3rem; border-radius: 4px;
          line-height: 1.5; transition: background .1s; flex-wrap: wrap;
        }
        .sm-node:hover { background: var(--hover-bg, rgba(122,162,247,.08)); }
        .sm-node.sm-hidden { display: none; }

        .sm-toggle {
          width: 1.2rem; height: 1.2rem; display: inline-flex;
          align-items: center; justify-content: center;
          cursor: pointer; border: none; background: none; color: inherit;
          font-size: .7rem; opacity: .6;
          transition: transform .18s, opacity .15s;
          flex-shrink: 0; padding: 0; border-radius: 3px;
        }
        .sm-toggle:hover { opacity: 1; background: var(--hover-bg, rgba(122,162,247,.15)); }
        .sm-toggle.open { transform: rotate(90deg); }
        .sm-toggle-placeholder { width: 1.2rem; flex-shrink: 0; }

        .sm-icon { font-size: .8rem; opacity: .5; flex-shrink: 0; }

        .sm-label a { color: var(--link, inherit); text-decoration: none; font-size: .9rem; }
        .sm-label a:hover { text-decoration: underline; }
        .sm-label span { font-size: .9rem; font-weight: 600; opacity: .85; }

        .sm-badge {
          font-size: .65rem; padding: .05rem .35rem; border-radius: 8px;
          background: var(--badge-bg, rgba(122,162,247,.15));
          color: var(--badge-fg, #7aa2f7); opacity: .8;
        }
        .sm-date { font-size: .75rem; opacity: .45; margin-left: .2rem; }
        .sm-tags { display: inline-flex; gap: .25rem; margin-left: .2rem; }
        .sm-tag {
          font-size: .65rem; padding: .05rem .3rem; border-radius: 8px;
          background: var(--tag-bg, rgba(160,200,120,.15));
          color: var(--tag-fg, #9ece6a); opacity: .85;
        }

        .sm-children { overflow: hidden; }
        .sm-children.collapsed { display: none; }
        .sm-label mark {
          background: var(--mark-bg, rgba(255,200,50,.3));
          color: inherit; border-radius: 2px; padding: 0 1px;
        }
        .sm-count { font-size: .78rem; opacity: .45; margin-top: .8rem; }
      `;
      document.head.appendChild(style);
    }

    // ── Widget scaffold ───────────────────────────────────────────────────
    const wrapper = document.createElement("div");
    wrapper.className = "sitemap-interactive";
    wrapper.innerHTML = `
      <div class="sm-toolbar">
        <input class="sm-search" type="search" placeholder="Filter pages…" aria-label="Filter" />
        <button class="sm-expand-all">⊞ Expand all</button>
        <button class="sm-collapse-all">⊟ Collapse all</button>
      </div>
      <div class="sm-tree" role="tree"></div>
      <p class="sm-count"></p>
    `;

    const treeEl = wrapper.querySelector(".sm-tree");
    const searchEl = wrapper.querySelector(".sm-search");
    const countEl = wrapper.querySelector(".sm-count");

    // ── Tree builder ──────────────────────────────────────────────────────
    function countLeaves(nodes) {
      let n = 0;
      for (const node of nodes) {
        if (!node.children || !node.children.length) n++;
        else n += countLeaves(node.children);
      }
      return n;
    }

    function buildTree(nodes) {
      const ul = document.createElement("ul");
      for (const node of nodes) {
        const li = document.createElement("li");
        const row = document.createElement("div");
        row.className = "sm-node";
        const hasChildren = node.children && node.children.length > 0;

        let childrenEl;
        if (hasChildren) {
          const toggle = document.createElement("button");
          toggle.className = "sm-toggle open";
          toggle.innerHTML = "▶";
          toggle.setAttribute("aria-expanded", "true");
          childrenEl = document.createElement("div");
          childrenEl.className = "sm-children";
          childrenEl.appendChild(buildTree(node.children));
          toggle.addEventListener("click", () => {
            childrenEl.classList.toggle("collapsed");
            toggle.classList.toggle("open");
            toggle.setAttribute("aria-expanded",
              String(!childrenEl.classList.contains("collapsed")));
          });
          row.appendChild(toggle);
        } else {
          const ph = document.createElement("span");
          ph.className = "sm-toggle-placeholder";
          row.appendChild(ph);
        }

        const icon = document.createElement("span");
        icon.className = "sm-icon";
        icon.textContent = hasChildren ? "📂" : "📄";
        row.appendChild(icon);

        const labelEl = document.createElement("span");
        labelEl.className = "sm-label";
        if (node.href) {
          const a = document.createElement("a");
          a.href = node.href;
          a.textContent = node.label;
          labelEl.appendChild(a);
        } else {
          const s = document.createElement("span");
          s.textContent = node.label;
          labelEl.appendChild(s);
        }
        row.appendChild(labelEl);

        if (hasChildren) {
          const badge = document.createElement("span");
          badge.className = "sm-badge";
          badge.textContent = countLeaves(node.children);
          row.appendChild(badge);
        }

        if (node.date) {
          const dateSpan = document.createElement("span");
          dateSpan.className = "sm-date";
          dateSpan.textContent = node.date;
          row.appendChild(dateSpan);
        }

        if (node.tags && node.tags.length) {
          const tagsEl = document.createElement("span");
          tagsEl.className = "sm-tags";
          node.tags.forEach((t) => {
            const tag = document.createElement("span");
            tag.className = "sm-tag";
            tag.textContent = t;
            tagsEl.appendChild(tag);
          });
          row.appendChild(tagsEl);
        }

        li.appendChild(row);
        if (hasChildren) li.appendChild(childrenEl);
        ul.appendChild(li);
      }
      return ul;
    }

    treeEl.appendChild(buildTree(tree));

    // ── Search / filter ───────────────────────────────────────────────────
    function escapeRe(s) {
      return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    }

    function filterTree(q) {
      const query = q.trim().toLowerCase();
      const re = query ? new RegExp(escapeRe(query), "gi") : null;
      let visible = 0;

      function walk(ul) {
        let anyVisible = false;
        for (const li of ul.children) {
          const row = li.querySelector(":scope > .sm-node");
          const childrenDiv = li.querySelector(":scope > .sm-children");
          const labelEl = row.querySelector(".sm-label");
          const target = labelEl.querySelector("a") || labelEl.querySelector("span");
          const text = target.dataset.orig || target.textContent;
          target.dataset.orig = text;

          let selfMatch = false;
          if (!query) {
            target.innerHTML = "";
            target.textContent = text;
            selfMatch = true;
          } else if (text.toLowerCase().includes(query)) {
            target.innerHTML = text.replace(re, (m) => `<mark>${m}</mark>`);
            selfMatch = true;
          } else {
            target.innerHTML = "";
            target.textContent = text;
          }

          let childVisible = false;
          if (childrenDiv) {
            childVisible = walk(childrenDiv.querySelector("ul"));
            if (query) {
              childrenDiv.classList.toggle("collapsed", !childVisible && !selfMatch);
              const toggle = row.querySelector(".sm-toggle");
              if (toggle) toggle.classList.toggle("open", childVisible || selfMatch);
            }
          }

          const show = !query || selfMatch || childVisible;
          row.classList.toggle("sm-hidden", !show);
          if (show) {
            anyVisible = true;
            if (!childrenDiv) visible++;
          }
        }
        return anyVisible;
      }

      walk(treeEl.querySelector("ul"));
      countEl.textContent = query
        ? `${visible} page${visible !== 1 ? "s" : ""} matching "${query}"`
        : "";
    }

    searchEl.addEventListener("input", (e) => filterTree(e.target.value));

    wrapper.querySelector(".sm-expand-all").addEventListener("click", () => {
      treeEl.querySelectorAll(".sm-children").forEach((el) => el.classList.remove("collapsed"));
      treeEl.querySelectorAll(".sm-toggle").forEach((el) => {
        el.classList.add("open");
        el.setAttribute("aria-expanded", "true");
      });
    });
    wrapper.querySelector(".sm-collapse-all").addEventListener("click", () => {
      treeEl.querySelectorAll(".sm-children").forEach((el) => el.classList.add("collapsed"));
      treeEl.querySelectorAll(".sm-toggle").forEach((el) => {
        el.classList.remove("open");
        el.setAttribute("aria-expanded", "false");
      });
    });

    // ── Inject into page ──────────────────────────────────────────────────
    if (parsed.wrapOutline) {
      // Hide original outline divs (direct children of content only)
      contentDiv.querySelectorAll(":scope > .outline-2, :scope > .outline-3")
        .forEach((el) => (el.style.display = "none"));
      // Insert after title + optional intro <p>
      const insertAfter = contentDiv.querySelector(":scope > p") || titleEl;
      insertAfter.insertAdjacentElement("afterend", wrapper);
    } else {
      parsed.replaceTarget.replaceWith(wrapper);
    }
  }

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