---
import GlossaryLayout from "../../layouts/GlossaryLayout.astro";
import GlossaryPortalAside from "../../components/GlossaryPortalAside.astro";
import GlossaryPortalHero from "../../components/GlossaryPortalHero.astro";
import GlossaryPortalSection from "../../components/GlossaryPortalSection.astro";
import GlossaryPortalStickySync from "../../components/GlossaryPortalStickySync.astro";
import { getCollection } from "astro:content";
import { hrefOfGlossaryEntry } from "../../lib/glossary";

const entries = await getCollection("glossaire");

const collator = new Intl.Collator("fr", { sensitivity: "base", numeric: true });

const kindLabels = {
  concept: "Concept",
  diagnostic: "Diagnostic",
  topologie: "Topologie",
  verbe: "Verbe",
  paradigme: "Paradigme",
  doctrine: "Doctrine",
  dispositif: "Dispositif",
  figure: "Figure",
  qualification: "Qualification",
  epistemologie: "Épistémologie",
};

const domainLabels = {
  transversal: "Transversal",
  theorie: "Théorie",
  "cas-ia": "Cas IA",
};

const levelLabels = {
  fondamental: "Fondamental",
  intermediaire: "Intermédiaire",
  avance: "Avancé",
};

const sorted = [...entries].sort((a, b) => collator.compare(a.data.term, b.data.term));

function groupByInitial(list) {
  const map = new Map();

  for (const entry of list) {
    const letter = (entry.data.term || "").trim().charAt(0).toUpperCase() || "#";
    if (!map.has(letter)) map.set(letter, []);
    map.get(letter).push(entry);
  }

  return [...map.entries()].sort((a, b) => collator.compare(a[0], b[0]));
}

const groupedAlpha = groupByInitial(sorted);
const totalEntries = sorted.length;
const totalLetters = groupedAlpha.length;
const paradigmesCount = entries.filter((entry) => entry.data.kind === "paradigme").length;
const doctrinesCount = entries.filter((entry) => entry.data.kind === "doctrine").length;
const verbesCount = entries.filter((entry) => entry.data.kind === "verbe").length;
const casIaCount = entries.filter((entry) => entry.data.domain === "cas-ia").length;

const pageItems = [
  { href: "#index-alphabetique", label: "Index alphabétique" },
  { href: "#prolonger-la-lecture", label: "Prolonger la lecture" },
];

const usefulLinks = [
  { href: "/glossaire/", label: "Accueil du glossaire" },
  { href: "/glossaire/concepts-fondamentaux/", label: "Concepts fondamentaux" },
  { href: "/glossaire/paradigme-archicratique/", label: "Paradigme archicratique" },
  { href: "/glossaire/scenes-archicratiques/", label: "Scènes archicratiques" },
  { href: "/glossaire/dynamiques-archicratiques/", label: "Dynamiques archicratiques" },
  { href: "/glossaire/tensions-irreductibles/", label: "Tensions irréductibles" },
  { href: "/glossaire/paradigmes/", label: "Paradigmes et doctrines" },
  { href: "/glossaire/archicrations/", label: "Méta-régimes archicratiques" },
];

const prolongerLinks = [
  {
    href: "/glossaire/",
    title: "Accueil du glossaire",
    text: "Revenir à la cartographie générale du système archicratique.",
  },
  {
    href: "/glossaire/concepts-fondamentaux/",
    title: "Concepts fondamentaux",
    text:
      "Repartir du noyau minimal : arcalité, cratialité, tension, archicration, co-viabilité, archicratie.",
  },
  {
    href: "/glossaire/paradigmes/",
    title: "Paradigmes et doctrines",
    text:
      "Retrouver le paysage théorique au sein duquel l’archicratie se situe et se distingue.",
  },
  {
    href: "/glossaire/archicrations/",
    title: "Méta-régimes archicratiques",
    text:
      "Parcourir les grandes formes de co-viabilité et leurs modulations historiques.",
  },
];
---

<GlossaryLayout
  title="Index complet du glossaire"
  version="1.0"
  stickyMode="glossary-portal"
>
  <Fragment slot="aside">
    <div class="gic-aside-stack">
      <GlossaryPortalAside
        ariaLabel="Navigation de l’index complet du glossaire"
        title="Index complet"
        meta={`${totalEntries} entrée${totalEntries > 1 ? "s" : ""} · ${totalLetters} lettre${totalLetters > 1 ? "s" : ""}`}
        pageItems={pageItems}
        usefulLinks={usefulLinks}
      />

      <div class="gic-aside__block">
        <h2 class="gic-aside__heading">Repères de lecture</h2>

        <div class="gic-aside__pills" aria-label="Repères de l’index">
          <span class="gic-aside__pill">
            {paradigmesCount} paradigme{paradigmesCount > 1 ? "s" : ""}
          </span>
          <span class="gic-aside__pill">
            {doctrinesCount} doctrine{doctrinesCount > 1 ? "s" : ""}
          </span>
          <span class="gic-aside__pill">
            {verbesCount} verbe{verbesCount > 1 ? "s" : ""}
          </span>
          <span class="gic-aside__pill">
            {casIaCount} entrée{casIaCount > 1 ? "s" : ""} cas IA
          </span>
        </div>

        <p class="gic-aside__note">
          Cet index complète les portails thématiques : il permet de retrouver
          rapidement une fiche, puis de repartir vers les grandes cartographies du glossaire.
        </p>
      </div>
    </div>
  </Fragment>

  <section class="gic-page">
    <GlossaryPortalHero
      prefix="gic"
      kicker="Référentiel terminologique"
      title="Index complet du glossaire"
      intro="Cette page rassemble l’ensemble des entrées du glossaire dans un ordre alphabétique intégral."
      moreParagraphs={[
        "Elle complète l’accueil conceptuel du glossaire par une navigation plus encyclopédique, plus directe et plus exhaustive.",
      ]}
      introMaxWidth="72ch"
      followIntroMaxWidth="68ch"
      moreMaxHeight="12rem"
    />

    <GlossaryPortalSection
      id="index-alphabetique"
      title="Index alphabétique"
      count={`${totalEntries} entrée${totalEntries > 1 ? "s" : ""}`}
      intro="Naviguer par lettre permet de retrouver rapidement chaque fiche du glossaire, tout en conservant ses principaux marqueurs de lecture."
    >
      <nav class="gic-letters" id="gic-letters-source" aria-label="Lettres de l’index">
        {groupedAlpha.map(([letter]) => (
          <a href={`#letter-${letter}`}>{letter}</a>
        ))}
      </nav>

      <nav
        class="gic-follow-letters"
        id="gic-follow-letters"
        aria-label="Lettres de l’index flottant"
        aria-hidden="true"
        hidden
      >
        {groupedAlpha.map(([letter]) => (
          <a href={`#letter-${letter}`}>{letter}</a>
        ))}
      </nav>
    </GlossaryPortalSection>

    <div class="gic-groups">
      {groupedAlpha.map(([letter, items]) => (
        <section class="gic-section gic-group" id={`letter-${letter}`}>
          <div class="gic-section__head">
            <h2>{letter}</h2>
            <span class="gic-section__count">
              {items.length} entrée{items.length > 1 ? "s" : ""}
            </span>
          </div>

          <div class="gic-list">
            {items.map((entry) => (
              <article class="gic-item">
                <a class="gic-term" href={hrefOfGlossaryEntry(entry)}>
                  {entry.data.term}
                </a>

                <p class="gic-def">{entry.data.definitionShort}</p>

                <p class="gic-meta">
                  <span>{kindLabels[entry.data.kind] ?? entry.data.kind}</span>
                  <span>{domainLabels[entry.data.domain] ?? entry.data.domain}</span>
                  <span>{levelLabels[entry.data.level] ?? entry.data.level}</span>
                </p>
              </article>
            ))}
          </div>
        </section>
      ))}
    </div>

    <GlossaryPortalSection
      id="prolonger-la-lecture"
      title="Prolonger la lecture"
      intro="Cet index intégral complète les portails thématiques sans s’y substituer. Il permet de repartir ensuite vers les grandes cartographies déjà stabilisées du glossaire."
      final={true}
    >
      <div class="gic-cards">
        {prolongerLinks.map((item) => (
          <a class="gic-card" href={item.href}>
            <strong>{item.title}</strong>
            <span>{item.text}</span>
          </a>
        ))}
      </div>
    </GlossaryPortalSection>
  </section>

  <GlossaryPortalStickySync
    heroMoreId="gic-hero-more"
    heroToggleId="gic-hero-toggle"
  />

  <script is:inline>
    (() => {
      const boot = () => {
        const body = document.body;
        const root = document.documentElement;
        const follow = document.getElementById("reading-follow");
        const lettersSource = document.getElementById("gic-letters-source");
        const lettersFollow = document.getElementById("gic-follow-letters");
        const groupSections = Array.from(document.querySelectorAll(".gic-group"));

        if (!body || !root || !follow || !lettersSource || !lettersFollow || groupSections.length === 0) {
          return;
        }

        const BODY_CLASS = "is-index-complet-page";
        const LETTERS_DOCKED_CLASS = "gic-letters-docked";
        const ACTIVE_CLASS = "is-active";
        const mqMobile = window.matchMedia("(max-width: 860px)");
        const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
        const EXTRA_GAP = 12;
        const MAX_CORRECTION_PASSES = 8;

        const sourceLinks = Array.from(lettersSource.querySelectorAll("a"));
        const followLinks = Array.from(lettersFollow.querySelectorAll("a"));
        const allLetterLinks = [...sourceLinks, ...followLinks];

        body.classList.add(BODY_CLASS);

        const computeFollowOn = () =>
          !mqMobile.matches &&
          follow.classList.contains("is-on") &&
          follow.style.display !== "none" &&
          follow.getAttribute("aria-hidden") !== "true";

        const setAnchorOffset = (value) => {
          root.style.setProperty(
            "--gic-follow-letters-offset",
            `${Math.max(0, Math.round(value))}px`
          );
        };

        const syncFollowLettersTop = () => {
          const inner = follow.querySelector(".reading-follow__inner");
          const followIsOn =
            follow.classList.contains("is-on") &&
            follow.style.display !== "none" &&
            follow.getAttribute("aria-hidden") !== "true";

          if (!followIsOn || !inner) {
            lettersFollow.style.top = "";
            return;
          }

          const followRect = follow.getBoundingClientRect();
          const innerRect = inner.getBoundingClientRect();
          const top = followRect.top + innerRect.height;

          lettersFollow.style.top = `${Math.round(top)}px`;
        };

        const syncLettersDockState = () => {
          if (mqMobile.matches || !computeFollowOn()) {
            body.classList.remove(LETTERS_DOCKED_CLASS);
            lettersFollow.hidden = true;
            lettersFollow.setAttribute("aria-hidden", "true");
            setAnchorOffset(0);
            return;
          }

          const inner = follow.querySelector(".reading-follow__inner");
          if (!inner) {
            body.classList.remove(LETTERS_DOCKED_CLASS);
            lettersFollow.hidden = true;
            lettersFollow.setAttribute("aria-hidden", "true");
            setAnchorOffset(0);
            return;
          }

          const sourceRect = lettersSource.getBoundingClientRect();
          const innerRect = inner.getBoundingClientRect();
          const shouldDock = sourceRect.top <= (innerRect.bottom + 6);

          body.classList.toggle(LETTERS_DOCKED_CLASS, shouldDock);
          lettersFollow.hidden = !shouldDock;
          lettersFollow.setAttribute("aria-hidden", shouldDock ? "false" : "true");

          if (shouldDock) {
            const dockedHeight = lettersFollow.getBoundingClientRect().height || 0;
            setAnchorOffset(dockedHeight + EXTRA_GAP);
          } else {
            setAnchorOffset(0);
          }
        };

        const getAnchorViewportTop = () => {
          const inner = follow.querySelector(".reading-follow__inner");
          const followOn = computeFollowOn();

          if (!followOn || !inner) {
            return 12;
          }

          const followLettersVisible =
            !lettersFollow.hidden &&
            lettersFollow.getAttribute("aria-hidden") === "false";

          if (followLettersVisible) {
            return Math.round(lettersFollow.getBoundingClientRect().bottom + EXTRA_GAP);
          }

          return Math.round(inner.getBoundingClientRect().bottom + EXTRA_GAP);
        };

        const getTargetHead = (section) =>
          section?.querySelector(".gic-section__head") || section;

        const correctAnchorAlignment = (section, pass = 0) => {
          if (!section || pass >= MAX_CORRECTION_PASSES) return;

          requestAnimationFrame(() => {
            syncFollowLettersTop();
            syncLettersDockState();

            const head = getTargetHead(section);
            if (!head) return;

            const wantedTop = getAnchorViewportTop();
            const headTop = head.getBoundingClientRect().top;
            const delta = headTop - wantedTop;

            if (Math.abs(delta) <= 1) return;

            window.scrollBy({
              top: delta,
              behavior: "auto",
            });

            correctAnchorAlignment(section, pass + 1);
          });
        };

        const scrollToLetterTarget = (targetId) => {
          const section = document.getElementById(targetId);
          if (!section) return;

          const head = getTargetHead(section);
          if (!head) return;

          syncFollowLettersTop();
          syncLettersDockState();

          const targetTop = getAnchorViewportTop();
          const rect = head.getBoundingClientRect();
          const absoluteTop = window.scrollY + rect.top - targetTop;

          if (window.location.hash !== `#${targetId}`) {
            history.pushState(null, "", `#${targetId}`);
          }

          window.scrollTo({
            top: Math.max(0, Math.round(absoluteTop)),
            behavior: reducedMotion.matches ? "auto" : "smooth",
          });

          requestAnimationFrame(() => {
            requestAnimationFrame(() => {
              correctAnchorAlignment(section, 0);
            });
          });

          window.setTimeout(() => correctAnchorAlignment(section, 0), 120);
          window.setTimeout(() => correctAnchorAlignment(section, 0), 260);
        };

        const syncActiveLetter = () => {
          let activeId = null;

          const inner = follow.querySelector(".reading-follow__inner");
          const followOn = computeFollowOn();
          const followBottom = followOn && inner
            ? inner.getBoundingClientRect().bottom
            : 0;

          const dockedHeight =
            !lettersFollow.hidden && lettersFollow.getAttribute("aria-hidden") === "false"
              ? lettersFollow.getBoundingClientRect().height
              : 0;

          const threshold = followBottom > 0
            ? followBottom + dockedHeight + 12
            : 140;

          for (const section of groupSections) {
            const rect = section.getBoundingClientRect();
            if (rect.top <= threshold) {
              activeId = section.id;
            } else {
              break;
            }
          }

          if (!activeId && groupSections[0]) {
            activeId = groupSections[0].id;
          }

          const applyState = (links) => {
            links.forEach((link) => {
              const isActive = link.getAttribute("href") === `#${activeId}`;
              link.classList.toggle(ACTIVE_CLASS, isActive);

              if (isActive) {
                link.setAttribute("aria-current", "true");
              } else {
                link.removeAttribute("aria-current");
              }
            });
          };

          applyState(sourceLinks);
          applyState(followLinks);
        };

        const handleLetterClick = (event) => {
          const link = event.currentTarget;
          const href = link?.getAttribute("href") || "";

          if (!href.startsWith("#letter-")) return;

          event.preventDefault();
          scrollToLetterTarget(href.slice(1));
        };

        allLetterLinks.forEach((link) => {
          link.addEventListener("click", handleLetterClick);
        });

        let raf = 0;
        const schedule = () => {
          if (raf) return;
          raf = requestAnimationFrame(() => {
            raf = 0;
            syncFollowLettersTop();
            syncLettersDockState();
            syncActiveLetter();
          });
        };

        const onScroll = () => {
          schedule();
        };

        const onHashChange = () => {
          const id = window.location.hash.replace(/^#/, "");
          if (!id.startsWith("letter-")) return;
          scrollToLetterTarget(id);
        };

        const followObserver = new MutationObserver(schedule);
        followObserver.observe(follow, {
          attributes: true,
          attributeFilter: ["class", "style", "aria-hidden"],
          subtree: false,
        });

        window.addEventListener("scroll", onScroll, { passive: true });
        window.addEventListener("resize", schedule);
        window.addEventListener("pageshow", schedule);
        window.addEventListener("hashchange", onHashChange);

        if (document.fonts?.ready) {
          document.fonts.ready.then(schedule).catch(() => {});
        }

        if (mqMobile.addEventListener) {
          mqMobile.addEventListener("change", schedule);
        } else if (mqMobile.addListener) {
          mqMobile.addListener(schedule);
        }

        if (reducedMotion.addEventListener) {
          reducedMotion.addEventListener("change", schedule);
        } else if (reducedMotion.addListener) {
          reducedMotion.addListener(schedule);
        }

        setAnchorOffset(0);
        schedule();

        if (window.location.hash.startsWith("#letter-")) {
          requestAnimationFrame(() => {
            requestAnimationFrame(() => {
              scrollToLetterTarget(window.location.hash.slice(1));
            });
          });
        }
      };

      if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", boot, { once: true });
      } else {
        boot();
      }
    })();
  </script>
</GlossaryLayout>

<style>
  .gic-page{
    --gic-follow-letters-offset: 0px;
    padding: 8px 0 24px;
  }

  .gic-aside-stack{
    display: flex;
    flex-direction: column;
    gap: 14px;
  }

  .gic-aside__block{
    border: 1px solid rgba(127,127,127,0.22);
    border-radius: 16px;
    padding: 12px;
    background: rgba(127,127,127,0.05);
  }

  .gic-aside__heading{
    margin: 0 0 10px;
    font-size: 13px;
    font-weight: 800;
    opacity: .9;
  }

  .gic-aside__pills{
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }

  .gic-aside__pill{
    display: inline-flex;
    align-items: center;
    padding: 4px 9px;
    border: 1px solid rgba(127,127,127,0.24);
    border-radius: 999px;
    background: rgba(127,127,127,0.04);
    font-size: 12px;
    line-height: 1.3;
    opacity: .9;
  }

  .gic-aside__note{
    margin: 10px 0 0;
    font-size: 12px;
    line-height: 1.45;
    opacity: .78;
  }

  .gic-letters{
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 14px;
  }

  .gic-letters a,
  .gic-follow-letters a{
    min-width: 34px;
    text-align: center;
    border: 1px solid rgba(127,127,127,0.24);
    border-radius: 10px;
    padding: 5px 8px;
    text-decoration: none;
    transition:
      transform 120ms ease,
      background 120ms ease,
      border-color 120ms ease,
      box-shadow 120ms ease,
      color 120ms ease;
  }

  .gic-letters a:hover,
  .gic-follow-letters a:hover{
    transform: translateY(-1px);
    background: rgba(127,127,127,0.08);
    border-color: rgba(0,217,255,0.16);
    text-decoration: none;
  }

  .gic-letters a.is-active,
  .gic-follow-letters a.is-active,
  .gic-letters a[aria-current="true"],
  .gic-follow-letters a[aria-current="true"]{
    border-color: rgba(0,217,255,0.34);
    background:
      linear-gradient(180deg, rgba(0,217,255,0.14), rgba(0,217,255,0.06)),
      rgba(127,127,127,0.10);
    color: var(--glossary-accent, #00d9ff);
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.05),
      0 0 0 1px rgba(0,217,255,0.06);
  }

  .gic-follow-letters{
    position: fixed;
    left: var(--reading-left);
    width: var(--reading-width);
    box-sizing: border-box;
    z-index: 59;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 8px 12px 10px;
    border: 1px solid rgba(127,127,127,.20);
    border-top: 0;
    border-radius: 0 0 14px 14px;
    background: rgba(255,255,255,.86);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    box-shadow: 0 10px 22px rgba(0,0,0,.06);
    opacity: 0;
    transform: translateY(-6px);
    pointer-events: none;
    transition: opacity 160ms ease, transform 160ms ease;
  }

  :global(body.is-index-complet-page.gic-letters-docked .gic-follow-letters){
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
  }

  :global(body.is-index-complet-page.gic-letters-docked #reading-follow .reading-follow__inner){
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }

  .gic-groups{
    display: flex;
    flex-direction: column;
    gap: 28px;
  }

  .gic-group{
    scroll-margin-top: calc(
      var(--sticky-offset-px, 96px) + 28px + var(--gic-follow-letters-offset, 0px)
    );
  }

  .gic-section{
    margin-top: 34px;
  }

  .gic-section__head{
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 10px;
    scroll-margin-top: calc(
      var(--sticky-offset-px, 96px) + 28px + var(--gic-follow-letters-offset, 0px)
    );
  }

  .gic-section__head h2{
    margin: 0;
    scroll-margin-top: calc(
      var(--sticky-offset-px, 96px) + 28px + var(--gic-follow-letters-offset, 0px)
    );
  }

  .gic-section__count{
    font-size: 13px;
    opacity: .72;
    white-space: nowrap;
  }

  .gic-list{
    display: grid;
    gap: 12px;
  }

  .gic-item{
    border: 1px solid rgba(127,127,127,0.20);
    border-radius: 16px;
    padding: 14px 16px;
    background: rgba(127,127,127,0.04);
  }

  .gic-term{
    display: inline-block;
    font-weight: 800;
    font-size: 1.04rem;
    line-height: 1.3;
    text-decoration: none;
    margin-bottom: 6px;
  }

  .gic-def{
    margin: 0 0 8px;
    line-height: 1.5;
    opacity: .94;
  }

  .gic-meta{
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0;
    font-size: 12px;
    opacity: .78;
  }

  .gic-meta span{
    border: 1px solid rgba(127,127,127,0.20);
    border-radius: 999px;
    padding: 2px 8px;
  }

  .gic-cards{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 12px;
    margin-top: 14px;
  }

  .gic-card{
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 14px 16px;
    border: 1px solid rgba(127,127,127,0.22);
    border-radius: 16px;
    background: rgba(127,127,127,0.05);
    text-decoration: none;
    transition: transform 120ms ease, background 120ms ease, border-color 120ms ease;
  }

  .gic-card:hover{
    transform: translateY(-1px);
    background: rgba(127,127,127,0.08);
    border-color: rgba(0,217,255,0.16);
    text-decoration: none;
  }

  .gic-card strong{
    font-size: 15px;
    line-height: 1.3;
  }

  .gic-card span{
    font-size: 14px;
    line-height: 1.45;
    opacity: .92;
  }

  @media (max-width: 860px){
    .gic-follow-letters{
      display: none !important;
    }
  }

  @media (prefers-color-scheme: dark){
    .gic-aside__block,
    .gic-aside__pill,
    .gic-item,
    .gic-card{
      background: rgba(255,255,255,0.04);
    }

    .gic-follow-letters{
      background: rgba(0,0,0,.58);
      box-shadow: 0 10px 22px rgba(0,0,0,.18);
    }

    .gic-letters a:hover,
    .gic-follow-letters a:hover,
    .gic-card:hover{
      background: rgba(255,255,255,0.07);
    }

    .gic-letters a.is-active,
    .gic-follow-letters a.is-active,
    .gic-letters a[aria-current="true"],
    .gic-follow-letters a[aria-current="true"]{
      background:
        linear-gradient(180deg, rgba(0,217,255,0.18), rgba(0,217,255,0.08)),
        rgba(255,255,255,0.06);
    }
  }
</style>