import classnames from 'classnames';
import {DragDropContext} from 'react-dnd';
import {injectIntl} from 'react-intl';
import HTML5Backend from 'react-dnd-html5-backend';
import React from 'react';

import SortableListItemDragLayer from './SortableListItemDragLayer';
import SortableListItem from './SortableListItem';

const methodsToBind = ['handleDrop', 'handleMove', 'handleMouseDown'];

class SortableList extends React.Component {
  constructor(props) {
    super(props);

    this.sortableListRef = null;
    this.state = {
      listOffset: null,
      items: props.items,
    };

    methodsToBind.forEach(method => {
      this[method] = this[method].bind(this);
    });
  }

  static getDerivedStateFromProps(props) {
    return {items: props.items};
  }

  handleDrop() {
    if (this.props.onDrop) {
      this.props.onDrop(this.state.items);
    }
  }

  handleMouseDown(event) {
    if (this.sortableListRef != null) {
      this.setState({
        listOffset: this.sortableListRef.getBoundingClientRect(),
      });
    }

    if (this.props.onMouseDown) {
      this.props.onMouseDown(event);
    }
  }

  handleMove(dragIndex, hoverIndex) {
    const {items} = this.state;
    const draggedItem = items[dragIndex];

    // Remove the item being dragged.
    items.splice(dragIndex, 1);
    // Add the item being dragged in its new position.
    items.splice(hoverIndex, 0, draggedItem);

    this.setState({items});

    if (this.props.onMove) {
      this.props.onMove(items);
    }
  }

  getItemList() {
    const {
      handleDrop,
      handleMove,
      state: {items},
      props: {lockedIDs, renderItem},
    } = this;

    return items.map((item, index) => {
      const {id, visible} = item;

      return (
        <SortableListItem
          id={id}
          index={index}
          isLocked={lockedIDs.includes(id)}
          isVisible={visible}
          key={id}
          onDrop={handleDrop}
          onMove={handleMove}>
          {renderItem(item, index)}
        </SortableListItem>
      );
    });
  }

  render() {
    const classes = classnames('sortable-list', this.props.className);

    return (
      <ul
        className={classes}
        onMouseDown={this.handleMouseDown}
        ref={ref => {
          this.sortableListRef = ref;
        }}>
        <SortableListItemDragLayer
          items={this.state.items}
          listOffset={this.state.listOffset}
          renderItem={this.props.renderItem}
        />
        {this.getItemList()}
      </ul>
    );
  }
}

export default DragDropContext(HTML5Backend)(injectIntl(SortableList));
