import ReactDOM from "react-dom";
import React, { useCallback, useContext, useMemo, useRef } from "react";
import { invariant } from "../../invariant";
import useDroppablePublisher from "../use-droppable-publisher";
import Placeholder from "../placeholder";
import AppContext from "../context/app-context";
import DroppableContext from "../context/droppable-context";
// import useAnimateInOut from '../use-animate-in-out/use-animate-in-out';
import getMaxWindowScroll from "../window/get-max-window-scroll";
import useValidation from "./use-validation";
import AnimateInOut from "../animate-in-out/animate-in-out";
import { PrivateDraggable } from "../draggable/draggable-api";

function getBody() {
  invariant(document.body, "document.body is not ready");
  return document.body;
}

export default function Droppable({
  mode = "standard",
  type = "DEFAULT",
  direction = "vertical",
  isDropDisabled = false,
  isCombineEnabled = false,
  ignoreContainerClipping = false,
  getContainerForClone = getBody,
  ...props
}) {
  const appContext = useContext(AppContext);
  invariant(appContext, "Could not find app context");
  const { contextId, isMovementAllowed } = appContext;
  const droppableRef = useRef(null);
  const placeholderRef = useRef(null);
  const {
    // own props
    children,
    droppableId,
    // map props
    snapshot,
    useClone,
    // dispatch props
    updateViewportMaxScroll
    // clone (ownProps)
  } = props;
  const getDroppableRef = useCallback(() => droppableRef.current, []);
  const setDroppableRef = useCallback((value) => {
    droppableRef.current = value;
  }, []);
  const getPlaceholderRef = useCallback(() => placeholderRef.current, []);
  const setPlaceholderRef = useCallback((value) => {
    placeholderRef.current = value;
  }, []);

  useValidation({
    props: { ...props, isDropDisabled, isCombineEnabled, ignoreContainerClipping },
    getDroppableRef,
    getPlaceholderRef
  });

  const onPlaceholderTransitionEnd = useCallback(() => {
    // A placeholder change can impact the window's max scroll
    if (isMovementAllowed()) {
      updateViewportMaxScroll({
        maxScroll: getMaxWindowScroll()
      });
    }
  }, [isMovementAllowed, updateViewportMaxScroll]);
  useDroppablePublisher({
    droppableId,
    type,
    mode,
    direction,
    isDropDisabled,
    isCombineEnabled,
    ignoreContainerClipping,
    getDroppableRef
  });
  const placeholder = /*#__PURE__*/ React.createElement(
    AnimateInOut,
    {
      on: props.placeholder,
      shouldAnimate: props.shouldAnimatePlaceholder
    },
    ({ onClose, data, animate }) =>
      /*#__PURE__*/ React.createElement(Placeholder, {
        placeholder: data,
        onClose: onClose,
        innerRef: setPlaceholderRef,
        animate: animate,
        contextId: contextId,
        onTransitionEnd: onPlaceholderTransitionEnd
      })
  );
  const provided = useMemo(
    () => ({
      innerRef: setDroppableRef,
      placeholder,
      droppableProps: {
        "data-rbd-droppable-id": droppableId,
        "data-rbd-droppable-context-id": contextId
      }
    }),
    [contextId, droppableId, placeholder, setDroppableRef]
  );
  const isUsingCloneFor = useClone ? useClone.dragging.draggableId : null;
  const droppableContext = useMemo(
    () => ({
      droppableId,
      type,
      isUsingCloneFor
    }),
    [droppableId, isUsingCloneFor, type]
  );

  function getClone() {
    if (!useClone) {
      return null;
    }
    const { dragging, render } = useClone;
    const node = /*#__PURE__*/ React.createElement(
      PrivateDraggable,
      {
        draggableId: dragging.draggableId,
        index: dragging.source.index,
        isClone: true,
        isEnabled: true,
        // not important as drag has already started
        shouldRespectForcePress: false,
        canDragInteractiveElements: true
      },
      (draggableProvided, draggableSnapshot) => render(draggableProvided, draggableSnapshot, dragging)
    );
    return /*#__PURE__*/ ReactDOM.createPortal(node, getContainerForClone());
  }

  return /*#__PURE__*/ React.createElement(
    DroppableContext.Provider,
    {
      value: droppableContext
    },
    children(provided, snapshot),
    getClone()
  );
}
