type MutableCurrent<T> = {
  current: T;
};

type AttachDocumentMouseDrag = (
  documentTarget: Document,
  onMove: (event: MouseEvent) => void,
  onUp: (event: MouseEvent) => void,
) => () => void;

type BeginAllocationDragSessionParams<TState> = {
  state: TState;
  cleanupRef: MutableCurrent<(() => void) | null>;
  stateRef: MutableCurrent<TState>;
  setState: (state: TState) => void;
  documentTarget: Document;
  attachDrag: AttachDocumentMouseDrag;
  updatePosition: (clientX: number) => void;
  finalize: (clientX: number) => Promise<void> | void;
};

export function beginAllocationDragSession<TState>({
  state,
  cleanupRef,
  stateRef,
  setState,
  documentTarget,
  attachDrag,
  updatePosition,
  finalize,
}: BeginAllocationDragSessionParams<TState>) {
  stateRef.current = state;
  setState(state);
  cleanupRef.current?.();

  function handleMove(event: MouseEvent) {
    updatePosition(event.clientX);
  }

  function handleUp(event: MouseEvent) {
    cleanupRef.current?.();
    cleanupRef.current = null;
    void finalize(event.clientX);
    event.preventDefault();
  }

  cleanupRef.current = attachDrag(documentTarget, handleMove, handleUp);
}
