import React from 'react';
import PropTypes from 'prop-types';
import { findDOMNode } from 'react-dom';
import { DragSource, DropTarget } from 'react-dnd';
import omit from 'lodash/omit';

const itemSource = {
  beginDrag(props) {
    return {
      index: props.index
    };
  },
  endDrag(props) {
    props.endDrag(props);
  }
};

const itemTarget = {
  hover(props, monitor, component) {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;

    // Don't replace items with themselves
    if (dragIndex === hoverIndex) {
      return;
    }

    // Determine rectangle on screen
    const hoverBoundingRect = findDOMNode(component).getBoundingClientRect(); // eslint-disable-line react/no-find-dom-node

    // Get vertical middle
    const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;

    // Determine mouse position
    const clientOffset = monitor.getClientOffset();

    // Get pixels to the top
    const hoverClientY = clientOffset.y - hoverBoundingRect.top;

    // Only perform the move when the mouse has crossed half of the items height
    // When dragging downwards, only move when the cursor is below 50%
    // When dragging upwards, only move when the cursor is above 50%

    // Dragging downwards
    if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
      return;
    }

    // Dragging upwards
    if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
      return;
    }

    // Time to actually perform the action
    props.moveItem(dragIndex, hoverIndex);

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    monitor.getItem().index = hoverIndex; // eslint-disable-line no-param-reassign
  }
};


const propTypes = {
  connectDragSource: PropTypes.func.isRequired,
  connectDropTarget: PropTypes.func.isRequired,
  isDragging: PropTypes.bool,
  className: PropTypes.string
};

const defaultProps = {
  isDragging: false,
  listName: 'list-item',
  className: undefined
};


const SortableItem = ({
  isDragging,
  connectDragSource,
  connectDropTarget,
  className,
  ...rest
}) => connectDragSource(connectDropTarget(
  <div
    style={{ opacity: isDragging ? '0' : '1' }}
    className={className}
    {...omit(rest, ['listName', 'index', 'moveItem', 'endDrag'])}
  />
));


SortableItem.propTypes = propTypes;
SortableItem.defaultProps = defaultProps;

export default DropTarget(
  props => props.listName,
  itemTarget,
  connect => ({
    connectDropTarget: connect.dropTarget()
  })
)(DragSource(
  props => props.listName,
  itemSource,
  (connect, monitor) => ({
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
  })
)(SortableItem));
