/**
 * Scroll-reveal & micro-interaction utilities
 * Uses Intersection Observer — no external libraries required.
 */

/**
 * Initialise scroll-reveal for all [data-animate] elements.
 * Call once per page after DOM is ready.
 */
export function initScrollReveal(): void {
  // Skip entirely when the user prefers reduced motion
  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
    document.querySelectorAll<HTMLElement>('[data-animate]').forEach((el) => {
      el.classList.add('is-visible');
    });
    return;
  }

  const observer = new IntersectionObserver(
    (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          entry.target.classList.add('is-visible');
          // Unobserve after triggering — animate once
          observer.unobserve(entry.target);
        }
      });
    },
    {
      threshold: 0.12,
      rootMargin: '0px 0px -40px 0px',
    }
  );

  document.querySelectorAll('[data-animate]').forEach((el) => observer.observe(el));
}

/**
 * Animates a numeric counter from 0 to `target`.
 * Attaches to elements with [data-counter] attribute.
 */
export function initCounters(): void {
  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

  const counterObserver = new IntersectionObserver(
    (entries) => {
      entries.forEach((entry) => {
        if (!entry.isIntersecting) return;
        const el = entry.target as HTMLElement;
        const target = parseInt(el.dataset.counter ?? '0', 10);
        const duration = parseInt(el.dataset.counterDuration ?? '2000', 10);
        const suffix = el.dataset.counterSuffix ?? '';
        animateCounter(el, target, duration, suffix);
        counterObserver.unobserve(el);
      });
    },
    { threshold: 0.5 }
  );

  document.querySelectorAll('[data-counter]').forEach((el) =>
    counterObserver.observe(el)
  );
}

function animateCounter(
  el: HTMLElement,
  target: number,
  duration: number,
  suffix: string
): void {
  const start = performance.now();
  const step = (now: number) => {
    const elapsed = now - start;
    const progress = Math.min(elapsed / duration, 1);
    // Ease-out cubic
    const eased = 1 - Math.pow(1 - progress, 3);
    const current = Math.floor(eased * target);
    el.textContent = current.toLocaleString() + suffix;
    el.classList.toggle('counting', progress < 1);
    if (progress < 1) requestAnimationFrame(step);
  };
  requestAnimationFrame(step);
}

/**
 * Adds a radial highlight that follows the cursor on `.btn-ripple` elements.
 */
export function initButtonRipple(): void {
  document.querySelectorAll<HTMLElement>('.btn-ripple').forEach((btn) => {
    btn.addEventListener('mousemove', (e: MouseEvent) => {
      const rect = btn.getBoundingClientRect();
      const x = ((e.clientX - rect.left) / rect.width) * 100;
      const y = ((e.clientY - rect.top) / rect.height) * 100;
      btn.style.setProperty('--x', `${x}%`);
      btn.style.setProperty('--y', `${y}%`);
    });
  });
}

/**
 * Fills `.progress-bar` elements to their `--progress-width` CSS var
 * once they scroll into view.
 */
export function initProgressBars(): void {
  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
    document.querySelectorAll<HTMLElement>('.progress-bar').forEach((el) => {
      el.classList.add('is-visible');
    });
    return;
  }

  const pbObserver = new IntersectionObserver(
    (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          entry.target.classList.add('is-visible');
          pbObserver.unobserve(entry.target);
        }
      });
    },
    { threshold: 0.4 }
  );

  document.querySelectorAll('.progress-bar').forEach((el) => pbObserver.observe(el));
}

/** Convenience: run all animation initialisers in one call. */
export function initAllAnimations(): void {
  initScrollReveal();
  initCounters();
  initButtonRipple();
  initProgressBars();
}
