import type { Position } from 'css-box-model';
import { invariant } from '../../../invariant';
import type {
  DroppableDimension,
  DraggableDimension,
  StateWhenUpdatesAllowed,
  DroppableId,
  DimensionMap,
  DragImpact,
  Viewport,
} from '../../../types';
import whatIsDraggedOver from '../../droppable/what-is-dragged-over';
import recomputeDisplacementVisibility from '../../update-displacement-visibility/recompute';
import getClientBorderBoxCenter from '../../get-center-from-impact/get-client-border-box-center';
import update from './update';

interface Args {
  state: StateWhenUpdatesAllowed;
  dimensions?: DimensionMap;
  viewport?: Viewport;
}

export default ({
  state,
  dimensions: forcedDimensions,
  viewport: forcedViewport,
}: // when a draggable is changing enabled state, sometimes it needs to force refresh an impact
Args): StateWhenUpdatesAllowed => {
  invariant(state.movementMode === 'SNAP');

  const needsVisibilityCheck: DragImpact = state.impact;
  const viewport: Viewport = forcedViewport || state.viewport;
  const dimensions: DimensionMap = forcedDimensions || state.dimensions;
  const { draggables, droppables } = dimensions;

  const draggable: DraggableDimension = draggables[state.critical.draggable.id];
  const isOver: DroppableId | null = whatIsDraggedOver(needsVisibilityCheck);
  invariant(isOver, 'Must be over a destination in SNAP movement mode');
  const destination: DroppableDimension = droppables[isOver];

  const impact: DragImpact = recomputeDisplacementVisibility({
    impact: needsVisibilityCheck,
    viewport,
    destination,
    draggables,
  });

  const clientSelection: Position = getClientBorderBoxCenter({
    impact,
    draggable,
    droppable: destination,
    draggables,
    viewport,
    afterCritical: state.afterCritical,
  });

  return update({
    // new
    impact,
    clientSelection,
    // pass through
    state,
    dimensions,
    viewport,
  });
};
