import { getNodeScroll, getScrollHeight, getScrollRect } from '../../lib/dom';
import { rafSchd } from '../../lib/rafSchd';

const SCROLL_SPEED = 10;
export const EDGE_SIZE = 50;

export const getAutoScrollingData = (
  clientY: number,
  scrollEl: Element | Window,
): {
  shouldScrolling: boolean;
  y: number;
} => {
  const scrollTop = Math.floor(getNodeScroll(scrollEl).scrollTop);

  const { relative, edges } = getScrollRect(scrollEl);
  const viewportHeight = relative.height;
  const documentHeight = getScrollHeight(scrollEl);
  const maxScrollY = documentHeight - viewportHeight;
  const canScrollUp = scrollTop > 0;
  const canScrollDown = scrollTop < maxScrollY;

  const [edgeTop, edgeBottom] = edges.y;
  const topDistance = clientY - edgeTop;
  const bottomDistance = edgeBottom - clientY;
  const isInTopEdge = topDistance <= EDGE_SIZE;
  const isInBottomEdge = bottomDistance <= EDGE_SIZE;

  const result = {
    shouldScrolling: (canScrollUp && isInTopEdge) || (canScrollDown && isInBottomEdge),
    y: 0,
  };

  // Inspired by https://github.com/SortableJS/Sortable/issues/1907#issuecomment-1495403785
  if (isInTopEdge) {
    result.y = -1 * ((EDGE_SIZE - topDistance) / EDGE_SIZE) * SCROLL_SPEED;
  } else if (isInBottomEdge) {
    result.y = ((EDGE_SIZE - bottomDistance) / EDGE_SIZE) * SCROLL_SPEED;
  }

  return result;
};

export type AutoScrollingDataFn = () => { shouldScrolling: boolean; y: number };

export const createAutoScrollController = (
  scrollEl: Element | Window,
): {
  tryAutoScroll: (fn: AutoScrollingDataFn) => void;
  stop: () => void;
  readonly isRunning: boolean;
} => {
  let isRunning = false;
  const scheduledScroll = rafSchd(scroll);

  function scroll(fn: AutoScrollingDataFn) {
    const { shouldScrolling, y } = fn();
    if (shouldScrolling) {
      isRunning = true;
      scrollEl.scrollBy(0, y);
      scheduledScroll(fn);
    } else {
      isRunning = false;
      scheduledScroll.cancel();
    }
  }

  const tryAutoScroll = (fn: AutoScrollingDataFn) => {
    scheduledScroll(fn);
  };

  const stop = () => {
    isRunning = false;
    scheduledScroll.cancel();
  };

  return {
    tryAutoScroll,
    stop,
    get isRunning() {
      return isRunning;
    },
  };
};
