/* ============================================================
   MÉRIDIAN — Chargement des produits sur les pages de listing
   ------------------------------------------------------------
   Mode piloté par le back-office (config catalogue), lu sur
   #js-product-list[data-load-mode] :
     - 'pagination' : pagination numérotée classique (ce script
                      ne fait rien, la pagination reste visible).
     - 'infinite'   : scroll infini (chargement auto en bas).
     - 'loadmore'   : bouton « Voir plus » (ajoute une page au clic).

   Mécanique commune : on récupère la page suivante via l'endpoint
   AJAX du listing (Accept: application/json → clé rendered_products)
   et on AJOUTE les produits à la grille existante.

   - La pagination reste dans le DOM (SEO + fallback sans JS),
     juste masquée (.mrd-has-loader), et sert de source du
     « lien suivant ».
   - Ré-init après chaque filtrage / tri (événement core
     "updateProductList", qui reconstruit #js-product-list).
   - Interactions des cartes (quick view, panier…) : déléguées
     sur <body> par le core → fonctionnent sur l'ajout dynamique.
   ============================================================ */
(function () {
  'use strict';

  var LIST_SEL = '#js-product-list';
  var GRID_SEL = '#js-product-list .products';
  var NEXT_SEL = '#js-product-list .pagination a.next:not(.disabled)';

  var observer = null;
  var ui = null;        // élément injecté : sentinelle (infinite) ou wrap du bouton (loadmore)
  var button = null;    // bouton « Voir plus » (mode loadmore)
  var nextUrl = null;
  var loading = false;
  var mode = 'pagination';
  var reqToken = 0;      // jeton : incrémenté à chaque re-filtrage/tri pour invalider les requêtes en vol
  var controller = null; // AbortController de la requête de page en cours

  function getList() { return document.querySelector(LIST_SEL); }

  function abs(href) {
    try { return new URL(href, window.location.href).href; } catch (e) { return href || null; }
  }

  function findNextUrl(scope) {
    var link = (scope || document).querySelector(NEXT_SEL);
    return link ? abs(link.getAttribute('href')) : null;
  }

  function setBusy(state) {
    loading = state;
    if (mode === 'infinite' && ui) ui.classList.toggle('is-loading', state);
    if (button) { button.disabled = state; button.classList.toggle('is-loading', state); }
  }

  function buildSentinel(list) {
    ui = document.createElement('div');
    ui.className = 'mrd-infinite-sentinel';
    ui.setAttribute('aria-hidden', 'true');
    ui.innerHTML = '<span class="mrd-infinite-spinner"></span>';
    insertBeforePagination(list, ui);
  }

  function buildButton(list) {
    var label = list.getAttribute('data-loadmore-label') || 'Voir plus';
    ui = document.createElement('div');
    ui.className = 'mrd-loadmore-wrap';
    button = document.createElement('button');
    button.type = 'button';
    button.className = 'btn btn-secondary mrd-loadmore';
    button.innerHTML = '<span class="mrd-loadmore-txt"></span><span class="mrd-infinite-spinner"></span>';
    button.querySelector('.mrd-loadmore-txt').textContent = label;
    button.addEventListener('click', loadNext);
    ui.appendChild(button);
    insertBeforePagination(list, ui);
  }

  function insertBeforePagination(list, el) {
    var pag = list.querySelector('.pagination');
    if (pag) list.insertBefore(el, pag); else list.appendChild(el);
  }

  function appendProducts(html) {
    var grid = document.querySelector(GRID_SEL);
    if (!grid) return false;
    var doc = new DOMParser().parseFromString(html, 'text/html');
    var newGrid = doc.querySelector(GRID_SEL);
    if (!newGrid) return false;
    var frag = document.createDocumentFragment();
    Array.prototype.forEach.call(newGrid.children, function (child) {
      frag.appendChild(document.importNode(child, true));
    });
    grid.appendChild(frag);
    nextUrl = findNextUrl(doc);
    return true;
  }

  // Stoppe et invalide toute requête de page en vol. Indispensable après un
  // filtrage/tri : la réponse d'une page « d'avant le filtre » ne doit JAMAIS
  // être ajoutée à la nouvelle grille (sinon des produits hors filtre s'affichent).
  function cancelInflight() {
    reqToken++;
    if (controller) { try { controller.abort(); } catch (e) {} controller = null; }
    loading = false;
  }

  function loadNext() {
    if (loading || !nextUrl) return;
    setBusy(true);
    var myToken = reqToken;
    var opts = {
      headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
      credentials: 'same-origin'
    };
    if ('AbortController' in window) {
      controller = new AbortController();
      opts.signal = controller.signal;
    }
    fetch(nextUrl, opts)
      .then(function (r) { return r.ok ? r.json() : Promise.reject(r.status); })
      .then(function (data) {
        if (myToken !== reqToken) return; // liste re-filtrée/triée entre-temps : réponse périmée, on ignore
        if (!data || !data.rendered_products || !appendProducts(data.rendered_products)) {
          nextUrl = null;
        }
        finish();
      })
      .catch(function () {
        if (myToken !== reqToken) return; // requête annulée par un re-filtrage : on ignore silencieusement
        // échec réseau / réponse inattendue : on réaffiche la pagination classique
        nextUrl = null;
        revealPagination();
        finish();
      });
  }

  function finish() {
    setBusy(false);
    if (!nextUrl) { teardown(); return; } // plus de page : on retire bouton/sentinelle
    // scroll infini : relance l'observation si le sentinelle est encore visible
    if (mode === 'infinite' && observer && ui) { observer.unobserve(ui); observer.observe(ui); }
  }

  function revealPagination() {
    var list = getList();
    if (list) list.classList.remove('mrd-has-loader');
  }

  function teardown() {
    if (observer) { observer.disconnect(); observer = null; }
    if (ui && ui.parentNode) ui.parentNode.removeChild(ui);
    ui = null;
    button = null;
  }

  function init() {
    cancelInflight(); // coupe tout chargement hérité de la liste précédente avant de repartir
    teardown();
    var list = getList();
    if (!list) return;

    mode = list.getAttribute('data-load-mode') || 'pagination';
    if (mode !== 'infinite' && mode !== 'loadmore') return; // pagination classique : rien à faire

    nextUrl = findNextUrl(document);
    if (!nextUrl) return; // une seule page : affichage standard conservé

    list.classList.add('mrd-has-loader'); // masque la pagination numérotée

    if (mode === 'infinite' && !('IntersectionObserver' in window)) {
      mode = 'loadmore'; // repli si pas d'IntersectionObserver
    }

    if (mode === 'loadmore') {
      buildButton(list);
      return;
    }

    // scroll infini
    buildSentinel(list);
    observer = new IntersectionObserver(function (entries) {
      for (var i = 0; i < entries.length; i++) {
        if (entries[i].isIntersecting) { loadNext(); break; }
      }
    }, { rootMargin: '600px 0px' });
    observer.observe(ui);
  }

  function boot() {
    init();
    // Ré-init après un filtrage / tri (le core remplace #js-product-list).
    if (window.prestashop && typeof window.prestashop.on === 'function') {
      window.prestashop.on('updateProductList', function () {
        cancelInflight();     // synchrone : invalide la requête en vol AVANT que sa réponse ne revienne
        setTimeout(init, 0);  // puis reconstruit le loader sur la liste filtrée fraîchement injectée
      });
    }
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', boot);
  } else {
    boot();
  }
})();
