// scripts/rehype-details-sections.mjs
// Rehype plugin: regroupe chaque section H2 en <details>/<summary> (accordion).
// FIX: anti-récursion / anti-boucle infinie -> post-order traversal (children first, then transform)
// et on ne retraverse pas les <details> insérés.

function isElement(node) {
    return node && node.type === "element" && typeof node.tagName === "string";
  }
  
  function headingLevel(node) {
    if (!isElement(node)) return null;
    const m = node.tagName.match(/^h([1-6])$/i);
    return m ? Number(m[1]) : null;
  }
  
  function nodeText(node) {
    if (!node) return "";
    if (node.type === "text") return String(node.value || "");
    if (isElement(node) && Array.isArray(node.children)) {
      return node.children.map(nodeText).join("");
    }
    return "";
  }
  
  function classList(v) {
    if (!v) return [];
    if (Array.isArray(v)) return v.map(String);
    if (typeof v === "string") return v.split(/\s+/).filter(Boolean);
    return [];
  }
  
  export default function rehypeDetailsSections(options = {}) {
    const {
      // Par défaut : sections sur H2 uniquement
      minDepth = 2,
      maxDepth = 2,
  
      openByDefault = false,
  
      detailsClass = "details-section",
      summaryClass = "details-summary",
      bodyClass = "details-body",
      anchorClass = "details-anchor",
    } = options;
  
    const DETAILS_SENTINEL_ATTR = "data-details-sections";
  
    function transformChildren(parent) {
      if (!parent || !Array.isArray(parent.children) || parent.children.length === 0) return;
  
      const children = parent.children;
      const out = [];
  
      for (let i = 0; i < children.length; i++) {
        const n = children[i];
        const lvl = headingLevel(n);
  
        // Wrap uniquement les headings min/max
        if (lvl && lvl >= minDepth && lvl <= maxDepth) {
          const heading = n;
  
          const props = heading.properties || {};
          const id = typeof props.id === "string" ? props.id : "";
          const title = nodeText(heading).trim() || "Section";
  
          // Capture jusqu’au prochain heading de niveau <= lvl
          const bodyNodes = [];
          i++;
          for (; i < children.length; i++) {
            const nxt = children[i];
            const nlvl = headingLevel(nxt);
            if (nlvl && nlvl <= lvl) {
              i--; // rendre la main au for principal
              break;
            }
            bodyNodes.push(nxt);
          }
  
          // On garde le heading dans le body mais on retire l'id
          // (l’ancre est portée par un <span id=...> au-dessus)
          const headingClone = {
            ...heading,
            properties: { ...(heading.properties || {}) },
          };
          if (headingClone.properties) delete headingClone.properties.id;
  
          const details = {
            type: "element",
            tagName: "details",
            properties: {
              [DETAILS_SENTINEL_ATTR]: "1",
              className: [detailsClass],
              ...(openByDefault ? { open: true } : {}),
              "data-section-level": String(lvl),
            },
            children: [
                {
                  type: "element",
                  tagName: "summary",
                  properties: { className: [summaryClass] },
                  children: [
                    ...(id ? [{
                      type: "element",
                      tagName: "span",
                      properties: { id, className: [anchorClass], "aria-hidden": "true" },
                      children: [],
                    }] : []),
                    (id
                      ? {
                          type: "element",
                          tagName: "a",
                          properties: { href: `#${id}` },
                          children: [{ type: "text", value: title }],
                        }
                      : { type: "text", value: title }),
                  ],
                },
                {
                  type: "element",
                  tagName: "div",
                  properties: { className: [bodyClass] },
                  children: [headingClone, ...bodyNodes],
                },
              ],              
          };
  
          out.push(details);
          continue;
        }
  
        out.push(n);
      }
  
      parent.children = out;
    }
  
    function walk(node) {
      if (!node) return;
  
      // IMPORTANT: si c’est un <details> déjà produit par nous,
      // on ne descend pas dedans (sinon boucle infinie).
      if (isElement(node) && node.tagName === "details") {
        const props = node.properties || {};
        if (props[DETAILS_SENTINEL_ATTR] === "1") return;
  
        // Si jamais quelqu’un a mis une class identique manuellement
        const cls = classList(props.className);
        if (cls.includes(detailsClass)) return;
      }
  
      // post-order : d’abord les enfants
      if (node.type === "root" || isElement(node)) {
        const kids = node.children || [];
        for (const k of kids) walk(k);
      }
  
      // puis on transforme le parent
      transformChildren(node);
    }
  
    return function transformer(tree) {
      walk(tree);
    };
  }