(() => {
  const slides = [...document.querySelectorAll('.slide')];
  const notesPanel = document.querySelector('.notes-panel');
  const downloadPanel = document.querySelector('.download-panel');
  const currentNode = document.querySelector('[data-current]');
  const totalNode = document.querySelector('[data-total]');
  const titleNode = document.querySelector('[data-slide-title]');
  const notesTitleNode = document.querySelector('[data-notes-title]');
  const notesCopyNode = document.querySelector('[data-notes-copy]');
  const progressNode = document.querySelector('[data-progress]');
  const notesButton = document.querySelector('[data-action="notes"]');
  const downloadsButton = document.querySelector('[data-action="downloads"]');
  const keyboardHint = document.querySelector('.keyboard-hint');
  let index = 0;
  let touchStartX = null;
  let pointerTimer = null;

  if (!slides.length) return;

  slides.forEach((slide, slideIndex) => {
    slide.id = `slide-${slideIndex + 1}`;
    slide.setAttribute('role', 'group');
    slide.setAttribute('aria-roledescription', 'slide');
    slide.setAttribute('aria-label', `${slideIndex + 1} of ${slides.length}: ${slide.dataset.label}`);
  });

  const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
  const pad = (value) => String(value).padStart(2, '0');

  function indexFromHash() {
    const match = window.location.hash.match(/^#slide-(\d+)$/);
    return match ? clamp(Number(match[1]) - 1, 0, slides.length - 1) : 0;
  }

  function updateScale() {
    const scale = Math.min(window.innerWidth / 1920, window.innerHeight / 1080);
    document.documentElement.style.setProperty('--slide-scale', String(scale));
  }

  function updateNotes(slide) {
    notesTitleNode.textContent = slide.dataset.label || '';
    notesCopyNode.textContent = slide.dataset.speakerNotes || 'No speaker notes for this slide.';
  }

  function showSlide(nextIndex, options = {}) {
    index = clamp(nextIndex, 0, slides.length - 1);
    slides.forEach((slide, slideIndex) => {
      const active = slideIndex === index;
      slide.classList.toggle('is-active', active);
      slide.setAttribute('aria-hidden', String(!active));
    });

    const activeSlide = slides[index];
    currentNode.textContent = pad(index + 1);
    totalNode.textContent = pad(slides.length);
    titleNode.textContent = activeSlide.dataset.label || '';
    progressNode.style.width = `${((index + 1) / slides.length) * 100}%`;
    updateNotes(activeSlide);

    if (!options.fromHash) {
      history.replaceState(null, '', `#slide-${index + 1}`);
    }
  }

  function next() {
    showSlide(index + 1);
  }

  function previous() {
    showSlide(index - 1);
  }

  function toggleNotes(force) {
    const shouldOpen = typeof force === 'boolean' ? force : !notesPanel.classList.contains('is-open');
    notesPanel.classList.toggle('is-open', shouldOpen);
    notesPanel.setAttribute('aria-hidden', String(!shouldOpen));
    notesButton.setAttribute('aria-pressed', String(shouldOpen));
    document.body.classList.toggle('has-focus-ui', shouldOpen);
    if (shouldOpen) notesPanel.querySelector('button').focus();
  }

  function toggleDownloads(force) {
    const shouldOpen = typeof force === 'boolean' ? force : !downloadPanel.open;
    if (shouldOpen && !downloadPanel.open) {
      downloadPanel.showModal();
      downloadsButton.setAttribute('aria-expanded', 'true');
      document.body.classList.add('has-focus-ui');
    } else if (!shouldOpen && downloadPanel.open) {
      downloadPanel.close();
    }
  }

  async function toggleFullscreen() {
    try {
      if (!document.fullscreenElement) {
        await document.documentElement.requestFullscreen({ navigationUI: 'hide' });
      } else {
        await document.exitFullscreen();
      }
    } catch {
      document.body.classList.toggle('is-presenting');
    }
  }

  function printDeck() {
    toggleDownloads(false);
    window.setTimeout(() => window.print(), 80);
  }

  function onAction(event) {
    const control = event.target.closest('[data-action]');
    if (!control) return;
    const action = control.dataset.action;
    if (action === 'next') next();
    if (action === 'previous') previous();
    if (action === 'notes') toggleNotes();
    if (action === 'notes-close') toggleNotes(false);
    if (action === 'downloads') toggleDownloads();
    if (action === 'downloads-close') toggleDownloads(false);
    if (action === 'fullscreen') toggleFullscreen();
    if (action === 'print') printDeck();
  }

  function onKeydown(event) {
    const activeElement = document.activeElement;
    const typing = activeElement && ['INPUT', 'TEXTAREA', 'SELECT'].includes(activeElement.tagName);
    const interactive = activeElement && ['A', 'BUTTON'].includes(activeElement.tagName);
    if (typing || downloadPanel.open || (interactive && [' ', 'Enter'].includes(event.key))) return;

    if (['ArrowRight', 'ArrowDown', 'PageDown', ' '].includes(event.key)) {
      event.preventDefault();
      next();
    }
    if (['ArrowLeft', 'ArrowUp', 'PageUp', 'Backspace'].includes(event.key)) {
      event.preventDefault();
      previous();
    }
    if (event.key === 'Home') showSlide(0);
    if (event.key === 'End') showSlide(slides.length - 1);
    if (event.key.toLowerCase() === 'f') toggleFullscreen();
    if (event.key.toLowerCase() === 's') toggleNotes();
    if (event.key.toLowerCase() === 'd') toggleDownloads();
    if (event.key === 'Escape' && notesPanel.classList.contains('is-open')) toggleNotes(false);
  }

  function onTouchStart(event) {
    touchStartX = event.changedTouches[0].screenX;
  }

  function onTouchEnd(event) {
    if (touchStartX === null) return;
    const delta = event.changedTouches[0].screenX - touchStartX;
    if (Math.abs(delta) > 55) delta < 0 ? next() : previous();
    touchStartX = null;
  }

  function revealPointerUi() {
    document.body.classList.add('has-focus-ui');
    window.clearTimeout(pointerTimer);
    pointerTimer = window.setTimeout(() => {
      if (!notesPanel.classList.contains('is-open') && !downloadPanel.open) {
        document.body.classList.remove('has-focus-ui');
      }
    }, 1800);
  }

  document.addEventListener('click', onAction);
  document.addEventListener('keydown', onKeydown);
  document.addEventListener('touchstart', onTouchStart, { passive: true });
  document.addEventListener('touchend', onTouchEnd, { passive: true });
  document.addEventListener('mousemove', revealPointerUi, { passive: true });
  window.addEventListener('resize', updateScale);
  window.addEventListener('hashchange', () => showSlide(indexFromHash(), { fromHash: true }));
  document.addEventListener('fullscreenchange', () => {
    document.body.classList.toggle('is-presenting', Boolean(document.fullscreenElement));
    updateScale();
  });
  downloadPanel.addEventListener('close', () => {
    downloadsButton.setAttribute('aria-expanded', 'false');
    if (!notesPanel.classList.contains('is-open')) document.body.classList.remove('has-focus-ui');
  });
  downloadPanel.addEventListener('click', (event) => {
    if (event.target === downloadPanel) toggleDownloads(false);
  });

  updateScale();
  showSlide(indexFromHash(), { fromHash: true });
  document.body.classList.remove('is-loading');
  window.setTimeout(() => keyboardHint.classList.add('is-hidden'), 7000);
})();
