import {
  ForwardedRef,
  forwardRef,
  useCallback,
  useEffect,
  useState,
} from "react";
import styles from "./ScrollContainer.module.scss";
import { subMonths } from "date-fns";

interface ScrollContainerProps<T> {
  direction: "horizontal" | "vertical";

  /** A list of things managed */
  list: T[];

  generatePreviousObject: () => void;
  generateNextObject: () => void;
}
/**
 * Generic infinite scrolling container
 */
function ScrollContainer<T>(
  { direction, list }: ScrollContainerProps<T>,
  ref: ForwardedRef<HTMLDivElement>
) {
  const [resetScrollToEl, setResetScrollToEl] = useState<
    [HTMLElement, number] | null
  >(null);

  // Viewport state
  const [viewportSize, setViewportSize] = useState<[number, number]>([0, 0]);
  const [viewportWidth, viewportHeight] = viewportSize;
  const viewportMetric =
    direction === "horizontal" ? viewportWidth : viewportHeight;

  // Scroll element state
  const [scrollyEl, setScrollyEl] = useState<HTMLDivElement | null>(null);
  const scrollyRef = useCallback((node: HTMLDivElement) => {
    if (node) {
      setScrollyEl(node);
      setViewportSize([node.clientWidth, node.clientHeight]);
    }
  }, []);

  // Add resize observer on the viewport
  useEffect(() => {
    const observer = new ResizeObserver((entries) => {
      if (entries && scrollyEl) {
        setViewportSize([scrollyEl.clientWidth, scrollyEl.clientHeight]);
      }
    });

    if (scrollyEl) observer.observe(scrollyEl);

    return () => {
      if (scrollyEl) observer.unobserve(scrollyEl);
    };
  }, [scrollyEl]);

  // Add intersection observer to tell when we scrolled too far
  useEffect(() => {
    if (!scrollyEl) return;

    const children: HTMLElement[] = [...scrollyEl.children];
    const firstChild = children[0];
    const lastChild = children[children.length - 1];

    const observer = new IntersectionObserver((entries) => {
      if (viewportMetric === 0) return;
      let newList = [...list];

      for (const entry of entries) {
        if (!entry.isIntersecting) continue;

        if (entry.target === firstChild) {
          console.log("intersected");
          const firstChildDate = new Date(firstChild.dataset.isodate!);
          const prevMonth = subMonths(firstChildDate, 1);
          newList = [prevMonth, ...monthsShown];
          newList = newMonthsShown.slice(0, NUM_MONTHS_SHOWN);

          setResetScrollToEl([
            firstChild,
            scrollyEl.scrollTop - firstChild.offsetTop,
          ]);
        } else if (entry.target === lastChild) {
          const lastChildDate = new Date(lastChild.dataset.isodate!);
          const nextMonth = addMonths(lastChildDate, 1);
          newMonthsShown = [...monthsShown, nextMonth];
          newMonthsShown = newMonthsShown.slice(-NUM_MONTHS_SHOWN);
        }
      }

      setMonthsShown(newMonthsShown);
    });
  });

  return <div className={styles.scrollyEl} ref={scrollyRef}></div>;
}

export default forwardRef(ScrollContainer);
