/**
 * Sapora — behaviours, one at a time.
 *
 *   1. Header      a hairline appears once the page has scrolled
 *   2. Dropdown    opens on hover on a mouse, on click on a touch screen
 *   3. Burger      the menu below 991px
 *   4. Reveals     IntersectionObserver, never animation-timeline
 *   5. Rows        the reviews and the photo strip are cloned so they loop
 *   6. Filter      says out loud how many dishes are left
 *
 * Nothing here is required to read the page: every block is visible, every
 * link works and the filter still filters with this file removed.
 */

/* ==========================================================================
   1. Header
   ========================================================================== */

{
  const bandeau = document.querySelector("[data-bandeau]");
  if (bandeau) {
    const marquer = () => bandeau.classList.toggle("est-detache", window.scrollY > 8);
    marquer();
    addEventListener("scroll", marquer, { passive: true });
  }
}

/* ==========================================================================
   2. Dropdown
   --------------------------------------------------------------------------
   A hover menu needs two delays, not one. Opening waits a moment so that
   crossing the item on the way somewhere else does not flash the panel open;
   closing waits longer, because the pointer has to travel over the empty gap
   between the trigger and the panel — the CSS bridge covers the pixels, this
   covers the time.
   ========================================================================== */

{
  const RETARD_OUVERTURE = 80;
  const RETARD_FERMETURE = 280;

  for (const item of document.querySelectorAll("[data-deroulant]")) {
    const bouton = item.querySelector("[data-deroulant-bouton]");
    if (!bouton) continue;

    let minuterie;
    const basculer = (ouvert) => {
      clearTimeout(minuterie);
      minuterie = setTimeout(
        () => bouton.setAttribute("aria-expanded", String(ouvert)),
        ouvert ? RETARD_OUVERTURE : RETARD_FERMETURE
      );
    };

    // Only on screens where the menu is a bar; below that it is a plain list.
    const surLarge = matchMedia("(min-width: 992px)");
    item.addEventListener("pointerenter", (e) => {
      if (e.pointerType !== "touch" && surLarge.matches) basculer(true);
    });
    item.addEventListener("pointerleave", (e) => {
      if (e.pointerType !== "touch" && surLarge.matches) basculer(false);
    });

    // Click always works: touch screens have no hover, and a keyboard needs
    // a way in that is not "move the mouse".
    bouton.addEventListener("click", () => {
      clearTimeout(minuterie);
      bouton.setAttribute("aria-expanded", bouton.getAttribute("aria-expanded") === "true" ? "false" : "true");
    });

    // Leaving the item with the keyboard closes it, but only once focus has
    // actually landed somewhere else.
    item.addEventListener("focusout", () => {
      requestAnimationFrame(() => {
        if (!item.contains(document.activeElement)) bouton.setAttribute("aria-expanded", "false");
      });
    });
  }

  addEventListener("keydown", (e) => {
    if (e.key !== "Escape") return;
    for (const b of document.querySelectorAll('[data-deroulant-bouton][aria-expanded="true"]')) {
      b.setAttribute("aria-expanded", "false");
      b.focus();
    }
  });
}

/* ==========================================================================
   3. Burger
   ========================================================================== */

{
  const bouton = document.querySelector("[data-nav-bouton]");
  const nav = document.querySelector("[data-nav]");

  if (bouton && nav) {
    const fermer = () => {
      bouton.setAttribute("aria-expanded", "false");
      bouton.setAttribute("aria-label", "Open menu");
    };

    bouton.addEventListener("click", () => {
      const ouvert = bouton.getAttribute("aria-expanded") === "true";
      bouton.setAttribute("aria-expanded", String(!ouvert));
      bouton.setAttribute("aria-label", ouvert ? "Open menu" : "Close menu");
    });

    // Following an anchor with the menu open would leave it hanging over the
    // section you just jumped to.
    nav.addEventListener("click", (e) => {
      if (e.target.closest("a")) fermer();
    });

    addEventListener("keydown", (e) => {
      if (e.key === "Escape" && bouton.getAttribute("aria-expanded") === "true") {
        fermer();
        bouton.focus();
      }
    });
  }
}

/* ==========================================================================
   4. Scroll reveals
   --------------------------------------------------------------------------
   The threshold is measured on the viewport, not on the element: a margin
   given as a percentage of the element would leave anything taller than the
   screen hidden for ever — a full-height panel never becomes "50% visible".
   `-15%` of the viewport height means a block starts appearing once it has
   cleared the bottom sixth of the screen.

   The safety net at the end of the page catches whatever the observer did
   not, so hitting the bottom can never leave an invisible block behind.
   ========================================================================== */

{
  const blocs = [...document.querySelectorAll("[data-reveal]")];

  const montrer = (el) => el.classList.add("est-visible");

  if (!("IntersectionObserver" in window)) {
    blocs.forEach(montrer);
  } else {
    const observateur = new IntersectionObserver(
      (entrees) => {
        for (const entree of entrees) {
          if (!entree.isIntersecting) continue;
          montrer(entree.target);
          observateur.unobserve(entree.target);
        }
      },
      { rootMargin: "0px 0px -15% 0px", threshold: 0 }
    );

    blocs.forEach((bloc) => observateur.observe(bloc));

    const filet = () => {
      if (innerHeight + scrollY < document.body.scrollHeight - 4) return;
      blocs.forEach(montrer);
      removeEventListener("scroll", filet);
    };
    addEventListener("scroll", filet, { passive: true });
  }
}

/* ==========================================================================
   5. Sliding rows
   --------------------------------------------------------------------------
   The animation walks the track back by exactly half its width, so the copy
   has to be a copy: same cards, same order, same gap. Cloning here instead of
   writing the cards twice in the HTML keeps one source for six reviews, and
   `inert` keeps the copy out of the tab order and off the accessibility tree.
   ========================================================================== */

for (const piste of document.querySelectorAll("[data-defile-piste]")) {
  const copie = piste.cloneNode(true);
  for (const enfant of [...copie.children]) {
    enfant.setAttribute("aria-hidden", "true");
    enfant.inert = true;
    piste.append(enfant);
  }
}

/* ==========================================================================
   6. Menu filter
   --------------------------------------------------------------------------
   The filtering itself is CSS (`:has()` on the section). This only reads the
   result out loud: a filter that changes the page silently leaves anyone not
   watching the grid with no idea that anything happened.
   ========================================================================== */

{
  const compte = document.querySelector("[data-filtre-compte]");
  const plats = [...document.querySelectorAll(".plat")];

  if (compte && plats.length) {
    for (const radio of document.querySelectorAll('input[name="cours"]')) {
      radio.addEventListener("change", () => {
        const visibles = plats.filter((plat) => plat.offsetParent !== null).length;
        compte.textContent = visibles === 1 ? "1 dish" : `${visibles} dishes`;
      });
    }
  }
}
