// astro.config.mjs
// @ts-check
import { defineConfig } from "astro/config";
import mdx from "@astrojs/mdx";
import sitemap from "@astrojs/sitemap";

import rehypeSlug from "rehype-slug";
import rehypeAutolinkHeadings from "rehype-autolink-headings";

import rehypeDetailsSections from "./scripts/rehype-details-sections.mjs";
import rehypeParagraphIds from "./src/plugins/rehype-paragraph-ids.js";

/**
 * Cast minimal pour satisfaire @ts-check sans dépendre de types internes Astro/Unified.
 * @param {unknown} x
 * @returns {any}
 */
const asAny = (x) => /** @type {any} */ (x);

/**
 * @param {any} node
 * @param {string} cls
 * @returns {boolean}
 */
function hasClass(node, cls) {
  const cn = node?.properties?.className;
  if (Array.isArray(cn)) return cn.includes(cls);
  if (typeof cn === "string") return cn.split(/\s+/).includes(cls);
  return false;
}

/**
 * Rehype plugin: retire les ids dupliqués en gardant en priorité:
 * 1) span.details-anchor
 * 2) h1..h6
 * 3) sinon: premier rencontré
 * @returns {(tree: any) => void}
 */
function rehypeDedupeIds() {
  /** @param {any} tree */
  return (tree) => {
    /** @type {Map<string, Array<{node:any, pref:number, idx:number}>>} */
    const occ = new Map();
    let idx = 0;

    /** @param {any} node */
    const walk = (node) => {
      if (!node || typeof node !== "object") return;

      if (node.type === "element") {
        const id = node.properties?.id;
        if (typeof id === "string" && id) {
          let pref = 2;
          if (node.tagName === "span" && hasClass(node, "details-anchor")) pref = 0;
          else if (/^h[1-6]$/.test(String(node.tagName || ""))) pref = 1;

          const arr = occ.get(id) || [];
          arr.push({ node, pref, idx: idx++ });
          occ.set(id, arr);
        }

        const children = node.children;
        if (Array.isArray(children)) for (const c of children) walk(c);
      } else if (Array.isArray(node.children)) {
        for (const c of node.children) walk(c);
      }
    };

    walk(tree);

    for (const [id, items] of occ.entries()) {
      if (items.length <= 1) continue;

      items.sort((a, b) => (a.pref - b.pref) || (a.idx - b.idx));
      const keep = items[0];

      for (let i = 1; i < items.length; i++) {
        const n = items[i].node;
        if (n?.properties?.id === id) delete n.properties.id;
      }

      // safety: on s'assure qu'un seul garde bien l'id
      if (keep?.node?.properties) keep.node.properties.id = id;
    }
  };
}

export default defineConfig({
  legacy: {
    collectionsBackwardsCompat: true,
  },
  
  output: "static",
  trailingSlash: "always",
  site: process.env.PUBLIC_SITE ?? "http://localhost:4321",

  integrations: [
    // Important: MDX hérite du pipeline markdown (ids p-… + autres plugins)
    mdx({ extendMarkdownConfig: true }),
    sitemap({
      filter: (page) => !page.includes("/api/") && !page.endsWith("/robots.txt"),
    }),
  ],

  markdown: {
    rehypePlugins: [
      asAny(rehypeSlug),
      [asAny(rehypeAutolinkHeadings), { behavior: "append" }],
      asAny(rehypeDetailsSections),
      asAny(rehypeParagraphIds),
      asAny(rehypeDedupeIds),
    ],
  },
});
