/**
 * Marenza — behaviours, one at a time.
 *
 *   1. Scroll reveal
 *   2. Sticky header
 *   3. Mobile menu
 *   4. Carousels (rooms and gallery)
 *   5. Accordion picture
 *   6. Newsletter
 *
 * Nothing here is required to read the page: every block only adds to markup
 * that already works. Delete this file and the site still scrolls, the
 * carousels still swipe, the accordion still opens.
 */
(function () {
  'use strict';

  var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  /* =======================================================================
     1. Scroll reveal
     -----------------------------------------------------------------------
     The threshold is measured on the viewport, not on the element: a element
     taller than the screen can never be "15% visible", and would stay hidden
     for ever. rootMargin does the measuring on the viewport instead.
     ======================================================================= */

  var revealables = document.querySelectorAll('[data-reveal], [data-reveal-group]');

  function showAll() {
    revealables.forEach(function (el) { el.classList.add('is-in'); });
  }

  if (!('IntersectionObserver' in window) || reduced) {
    showAll();
  } else {
    var observer = new IntersectionObserver(function (entries) {
      entries.forEach(function (entry) {
        if (!entry.isIntersecting) return;
        entry.target.classList.add('is-in');
        observer.unobserve(entry.target);
      });
    }, { rootMargin: '0px 0px -15% 0px', threshold: 0 });

    revealables.forEach(function (el) { observer.observe(el); });

    /* The safety net: whatever is left hidden when the bottom of the page is
       reached gets revealed. A short last section can otherwise never cross
       the -15% line. */
    window.addEventListener('scroll', function () {
      if (window.scrollY + window.innerHeight < document.documentElement.scrollHeight - 4) return;
      showAll();
    }, { passive: true });
  }

  /* =======================================================================
     2. Sticky header
     Past 2% of the page the bar takes a background — the page-scroll
     interaction of the original, rewritten as one class.
     ======================================================================= */

  var header = document.getElementById('header');

  if (header) {
    var onScroll = function () {
      var limit = document.documentElement.scrollHeight * 0.02;
      header.classList.toggle('is-stuck', window.scrollY > Math.min(limit, 120));
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
  }

  /* =======================================================================
     3. Mobile menu
     ======================================================================= */

  var toggle = document.querySelector('.nav__toggle');
  var menu = document.getElementById('nav-menu');

  if (toggle && menu) {
    toggle.addEventListener('click', function () {
      var open = toggle.getAttribute('aria-expanded') === 'true';
      toggle.setAttribute('aria-expanded', String(!open));
      menu.classList.toggle('is-open', !open);
    });

    /* Any link inside closes it again. */
    menu.addEventListener('click', function (event) {
      if (event.target.closest('a')) {
        toggle.setAttribute('aria-expanded', 'false');
        menu.classList.remove('is-open');
      }
    });

    document.addEventListener('keydown', function (event) {
      if (event.key !== 'Escape') return;
      toggle.setAttribute('aria-expanded', 'false');
      menu.classList.remove('is-open');
    });
  }

  /* The "Pages" disclosure closes when the pointer leaves it or focus goes
     elsewhere. Two delays, not one: the closing delay has to cover the trip
     across the empty gap between the trigger and the panel. */
  var pages = document.querySelector('.nav__pages');

  if (pages && window.matchMedia('(min-width: 992px)').matches) {
    var openTimer = null;
    var closeTimer = null;

    var openPages = function () {
      clearTimeout(closeTimer);
      openTimer = setTimeout(function () { pages.open = true; }, 60);
    };
    var closePages = function () {
      clearTimeout(openTimer);
      closeTimer = setTimeout(function () { pages.open = false; }, 320);
    };

    pages.addEventListener('mouseenter', openPages);
    pages.addEventListener('mouseleave', closePages);
    pages.addEventListener('focusout', function (event) {
      if (!pages.contains(event.relatedTarget)) pages.open = false;
    });
  }

  /* =======================================================================
     4. Carousels
     -----------------------------------------------------------------------
     The scrolling itself is the browser's: the track is a scroll container
     with scroll-snap. This only marks the card closest to the middle, so the
     others can shrink, and keeps the arrow and the dots in step.
     ======================================================================= */

  function markMiddle(track, items) {
    var box = track.getBoundingClientRect();
    var middle = box.left + box.width / 2;
    var best = null;
    var bestGap = Infinity;

    items.forEach(function (item) {
      var r = item.getBoundingClientRect();
      var gap = Math.abs(r.left + r.width / 2 - middle);
      if (gap < bestGap) { bestGap = gap; best = item; }
    });

    items.forEach(function (item) { item.classList.toggle('is-active', item === best); });
    return items.indexOf(best);
  }

  /* Dragging with a mouse.
     A scroll container answers the wheel, the trackpad, a finger and the
     keyboard on its own — but not a mouse held down and moved. That is the one
     gesture this adds. Snapping is switched off for the length of the drag,
     otherwise it fights every pixel; on release it settles the card back. */
  function dragToScroll(track, indexActuel, apresGlissement) {
    var tenu = false;
    var departX = 0;
    var departScroll = 0;
    var departIndex = 0;
    var course = 0;

    track.addEventListener('pointerdown', function (event) {
      /* Touch and pen already scroll natively — taking over would be worse. */
      if (event.pointerType !== 'mouse' || event.button !== 0) return;
      tenu = true;
      course = 0;
      departX = event.clientX;
      departScroll = track.scrollLeft;
      departIndex = indexActuel();
      track.classList.add('is-dragging');
    });

    track.addEventListener('pointermove', function (event) {
      if (!tenu) return;
      course = event.clientX - departX;
      /* Past a few pixels the gesture is a drag, not a click: capture the
         pointer so leaving the track does not drop it mid-movement. */
      if (Math.abs(course) > 3 && !track.hasPointerCapture(event.pointerId)) {
        track.setPointerCapture(event.pointerId);
      }
      track.scrollLeft = departScroll - course;
    });

    var relacher = function (event) {
      if (!tenu) return;
      tenu = false;
      if (event && track.hasPointerCapture(event.pointerId)) {
        track.releasePointerCapture(event.pointerId);
      }
      track.classList.remove('is-dragging');
      apresGlissement(course, departIndex);
    };

    track.addEventListener('pointerup', relacher);
    track.addEventListener('pointercancel', relacher);
    track.addEventListener('lostpointercapture', relacher);
  }

  function setUpCarousel(trackId, onIndex) {
    var track = document.getElementById(trackId);
    if (!track) return null;

    var items = Array.prototype.slice.call(track.children);
    var frame = null;

    var update = function () {
      if (frame) return;
      frame = requestAnimationFrame(function () {
        frame = null;
        var index = markMiddle(track, items);
        if (onIndex) onIndex(index);
      });
    };

    track.addEventListener('scroll', update, { passive: true });
    window.addEventListener('resize', update);
    update();

    var api = {
      track: track,
      items: items,
      goTo: function (index) {
        var target = items[Math.max(0, Math.min(items.length - 1, index))];
        if (!target) return;
        var offset = target.offsetLeft - (track.clientWidth - target.offsetWidth) / 2;
        track.scrollTo({ left: offset, behavior: reduced ? 'auto' : 'smooth' });
      },
      current: function () { return items.indexOf(track.querySelector('.is-active')); },
    };

    /* A card is a thousand pixels wide: leaving it to snapping alone would
       mean dragging across half of it before anything moves. Past 60 px the
       gesture is a decision, so it changes card. */
    dragToScroll(track, api.current, function (course, departIndex) {
      if (Math.abs(course) < 60) return;
      if (api.current() !== departIndex) return;
      api.goTo(departIndex + (course < 0 ? 1 : -1));
    });

    return api;
  }

  var rooms = setUpCarousel('rooms-track');

  document.querySelectorAll('[data-carousel]').forEach(function (button) {
    button.addEventListener('click', function () {
      if (!rooms) return;
      var step = Number(button.getAttribute('data-step')) || 1;
      var next = rooms.current() + step;
      /* One long carousel with one arrow: past the last card it wraps. */
      if (next >= rooms.items.length) next = 0;
      if (next < 0) next = rooms.items.length - 1;
      rooms.goTo(next);
    });
  });

  var dots = Array.prototype.slice.call(document.querySelectorAll('#gallery-dots button'));

  var gallery = setUpCarousel('gallery-track', function (index) {
    dots.forEach(function (dot, i) {
      dot.setAttribute('aria-current', String(i === index));
    });
  });

  dots.forEach(function (dot, i) {
    dot.addEventListener('click', function () { if (gallery) gallery.goTo(i); });
  });

  /* =======================================================================
     5. Accordion picture
     The <details> group is exclusive on its own (the `name` attribute). This
     only swaps the photograph on the right to match the open panel.
     ======================================================================= */

  var list = document.getElementById('experiences-list');
  var figure = document.getElementById('experiences-figure');

  if (list && figure) {
    var panels = Array.prototype.slice.call(list.querySelectorAll('details'));
    var pictures = Array.prototype.slice.call(figure.querySelectorAll('img'));

    panels.forEach(function (panel, i) {
      panel.addEventListener('toggle', function () {
        if (!panel.open) return;
        pictures.forEach(function (img, j) { img.classList.toggle('is-shown', i === j); });
      });
    });
  }

  /* =======================================================================
     6. Newsletter
     A template ships without a server. Rather than pretend, the form checks
     the address and says plainly that nothing was sent anywhere.
     ======================================================================= */

  var form = document.getElementById('newsletter');
  var note = document.getElementById('newsletter-note');

  if (form && note) {
    form.addEventListener('submit', function (event) {
      event.preventDefault();
      var field = form.querySelector('input[type="email"]');
      var value = field.value.trim();

      if (!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(value)) {
        note.textContent = 'Please enter a valid email address.';
        note.setAttribute('data-state', 'error');
        field.focus();
        return;
      }

      note.textContent = 'Thank you — this demo form stores nothing. Connect it to your own list.';
      note.setAttribute('data-state', 'ok');
      form.reset();
    });
  }
})();
