import { useMemo, useState, useCallback, useEffect } from 'react';

/**
 * A hook for keeping track of which step the user is at in a scrolly section.
 * This hook assumes that the scrolly progress is determined by which slide is
 * visible on the screen; each slide node is expected to set a
 * `data-scrollindex` attribute specifying which index should be active when
 * that slide is visible. By default this hook will look for any nodes with
 * the `data-scrollindex` attribute set, but you can specify another selector
 * to use in case you have multiple scroll sections and don't want them to
 * conflict. You can also pass in custom `rootMargin` and `threshold` options
 * to pass to the intersection observer and an additional dependencies array
 * to trigger the hook to rediscover slides when something changes. The
 * dependencies can be helpful in case you're waiting on some data to load
 * before rendering the slides.
 */
const useActiveScrollSection = function useActiveScrollSection(opts = {}) {
  const {
    selector = '[data-scrollindex]',
    rootMargin = '-10%',
    threshold,
    dependencies = []
  } = opts;
  const [indexes, setIndexes] = useState({});
  const [previous, setPrevious] = useState();
  const registerIndex = useCallback(({
    index,
    active,
    state
  }) => {
    setIndexes(old => ({
      ...old,
      [index]: {
        index,
        active,
        state
      }
    }));
  }, []);
  const activeIndex = useMemo(() => {
    const ids = Object.values(indexes);
    if (ids.length < 1) return undefined;
    const max = (...args) => Math.max(...args.filter(d => d !== undefined));
    let maxActive;
    let maxPast;
    ids.forEach(id => {
      if (id.state === 'active') {
        maxActive = max(maxActive, id.index);
      }
      if (id.state === 'past') {
        maxPast = max(maxPast, id.index);
      }
    });
    if (maxActive !== undefined) return maxActive;
    if (maxPast !== undefined) return maxPast;
    return 0;
  }, [indexes]);
  useEffect(() => {
    const options = {
      rootMargin,
      threshold
    };
    const getState = ({
      active,
      half
    }) => {
      if (active) return 'active';
      if (half === 'top') return 'past';
      return 'future';
    };
    const handler = entries => {
      const mid = window.innerHeight / 2;
      entries.forEach(entry => {
        const index = parseInt(entry.target.dataset.scrollindex, 10);
        const active = entry.isIntersecting;
        const {
          top
        } = entry.boundingClientRect;
        registerIndex({
          index,
          active,
          state: getState({
            active,
            half: top > mid ? 'bottom' : 'top'
          })
        });
      });
    };
    const observer = new window.IntersectionObserver(handler, options);
    const nodes = window.document.querySelectorAll(selector);
    nodes.forEach(node => {
      observer.observe(node);
    });
    return () => {
      observer.disconnect();
    };
  }, [selector, registerIndex, selector, rootMargin, threshold, ...dependencies]);

  // Set at end of effect
  useEffect(() => () => {
    setPrevious(activeIndex);
  }, [activeIndex]);
  return {
    activeIndex,
    previousIndex: previous === undefined ? activeIndex : previous
  };
};
export default useActiveScrollSection;