import memoizeOne from 'memoize-one';
import type { Position } from 'css-box-model';
import type {
  DroppableDimension,
  DroppableDimensionMap,
  DroppableId,
} from '../../../types';
import { invariant } from '../../../invariant';
import isPositionInFrame from '../../visibility/is-position-in-frame';
import { toDroppableList } from '../../dimension-structures';

const getScrollableDroppables = memoizeOne(
  (droppables: DroppableDimensionMap): DroppableDimension[] =>
    toDroppableList(droppables).filter(
      (droppable: DroppableDimension): boolean => {
        // exclude disabled droppables
        if (!droppable.isEnabled) {
          return false;
        }

        // only want droppables that are scrollable
        if (!droppable.frame) {
          return false;
        }

        return true;
      },
    ),
);

const getScrollableDroppableOver = (
  target: Position,
  droppables: DroppableDimensionMap,
): DroppableDimension | null => {
  const maybe =
    getScrollableDroppables(droppables).find(
      (droppable: DroppableDimension): boolean => {
        invariant(droppable.frame, 'Invalid result');

        return isPositionInFrame(droppable.frame.pageMarginBox)(target);
      },
    ) || null;

  return maybe;
};

interface Api {
  center: Position;
  destination: DroppableId | null;
  droppables: DroppableDimensionMap;
}

export default ({
  center,
  destination,
  droppables,
}: Api): DroppableDimension | null => {
  // We need to scroll the best droppable frame we can so that the
  // placeholder buffer logic works correctly

  if (destination) {
    const dimension: DroppableDimension = droppables[destination];
    if (!dimension.frame) {
      return null;
    }
    return dimension;
  }

  // 2. If we are not over a droppable - are we over a droppable frame?
  const dimension: DroppableDimension | null = getScrollableDroppableOver(
    center,
    droppables,
  );

  return dimension;
};
