import { invariant } from '../invariant';
import getHomeLocation from './get-home-location';
import type {
  DraggableDimension,
  DroppableDimension,
  DraggableDimensionMap,
  DragImpact,
  DisplacedBy,
  Viewport,
  DraggableIdMap,
  DisplacementGroups,
  LiftEffect,
} from '../types';
import getDraggablesInsideDroppable from './get-draggables-inside-droppable';
import getDisplacedBy from './get-displaced-by';
import getDisplacementGroups from './get-displacement-groups';

interface Args {
  draggable: DraggableDimension;
  home: DroppableDimension;
  draggables: DraggableDimensionMap;
  viewport: Viewport;
}

interface Result {
  afterCritical: LiftEffect;
  impact: DragImpact;
}

export default ({ draggable, home, draggables, viewport }: Args): Result => {
  const displacedBy: DisplacedBy = getDisplacedBy(
    home.axis,
    draggable.displaceBy,
  );

  const insideHome: DraggableDimension[] = getDraggablesInsideDroppable(
    home.descriptor.id,
    draggables,
  );

  // in a list that does not start at 0 the descriptor.index might be different from the index in the list
  // eg a list could be: [2,3,4]. A descriptor.index of '2' would actually be in index '0' of the list
  const rawIndex: number = insideHome.indexOf(draggable);
  invariant(rawIndex !== -1, 'Expected draggable to be inside home list');

  const afterDragging: DraggableDimension[] = insideHome.slice(rawIndex + 1);
  const effected: DraggableIdMap = afterDragging.reduce(
    (previous: DraggableIdMap, item: DraggableDimension): DraggableIdMap => {
      previous[item.descriptor.id] = true;
      return previous;
    },
    {},
  );
  const afterCritical: LiftEffect = {
    inVirtualList: home.descriptor.mode === 'virtual',
    displacedBy,
    effected,
  };

  const displaced: DisplacementGroups = getDisplacementGroups({
    afterDragging,
    destination: home,
    displacedBy,
    last: null,
    viewport: viewport.frame,
    // originally we do not want any animation as we want
    // everything to be fixed in the same position that
    // it started in
    forceShouldAnimate: false,
  });

  const impact: DragImpact = {
    displaced,
    displacedBy,
    at: {
      type: 'REORDER',
      destination: getHomeLocation(draggable.descriptor),
    },
  };
  return { impact, afterCritical };
};
