import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import enhanceWithClickOutside from 'react-click-outside';

import '../base';
import styles from './MultiSelect.styl';
import checkboxStyles from '../atoms/Checkbox.styl';
import newId from '../utils/newId';

import Collapsible from '../atoms/Collapsible';
import Button from '../atoms/Button';
import MultiSelectGroup from '../atoms/MultiSelectGroup';
import Label from '../atoms/Label';

const removeDefault = (selected) => selected.filter((value) => value !== 'default');

class MultiSelect extends Component {
  constructor(props) {
    super(props);

    const defaultSelected = props.defaultSelected;
    const selected = defaultSelected.length ? defaultSelected : ['default'];

    this.state = {
      open: props.open,
      selected
    };

    this.toggleOpen = this.toggleOpen.bind(this);
    this.handleOnChange = this.handleOnChange.bind(this);
  }

  componentWillMount() {
    const { onBeforeMount } = this.props;

    this.id = newId();

    if (onBeforeMount) {
      onBeforeMount(this.id);
    }
  }

  componentWillUnmount() {
    const { onBeforeUnmount } = this.props;

    if (onBeforeUnmount) {
      onBeforeUnmount(this.id);
    }
  }

  handleOnChange(values) {
    const selected = values.length ? values : ['default'];
    this.setState({ selected });

    // only fire change event in case the onChange props has been defined and the selected state changed
    const { onBeforeChange, onChange, onAfterChange } = this.props;

    if (JSON.stringify(this.state.selected) !== JSON.stringify(selected)) {
      if (onBeforeChange) onBeforeChange(removeDefault(selected), this.id);
      if (onChange) onChange(removeDefault(selected), this.id);
      if (onAfterChange) onAfterChange(removeDefault(selected), this.id);
    }
  }

  toggleOpen(state) {
    const open = typeof state === 'boolean' ? state : !this.state.open;

    this.setState({ open });

    if (this.props.onToggle) {
      this.props.onToggle(open, removeDefault(this.state.selected));
    }
  }

  handleClickOutside() {
    if (this.state.open === true) {
      this.toggleOpen(false);
    }
  }

  render() {
    const { children, label, placeholder, maxHeight, inline } = this.props;
    const { open, selected } = this.state;

    const containerClasses = classNames({
      [checkboxStyles.filter]: true,
      [styles.container]: true,
      [styles.open]: open
    });

    const collapsibleClasses = classNames({
      [styles.collapsible]: true,
      [styles.overlapping]: !inline
    });

    const labelClasses = classNames({
      [styles.label]: true
    });

    // get labels
    const childrenArray = React.Children.toArray(children);

    const selectedLabels = childrenArray.reduce((accumulator, group) => {
      const innerChildren = React.Children.toArray(group.props.children);
      const isSelected = (child) => selected.includes(child.props.value);

      return [
        ...accumulator,
        ...innerChildren
          .filter(isSelected)
          .map((child) => child.props.label)
      ];
    }, []).join(', ');

    const childProps = { onChange: this.handleOnChange, selected };

    return (
      <div className={containerClasses}>
        {
          label ? <Label className={labelClasses}>{label}</Label> : null
        }
        <Button onClick={this.toggleOpen} className={styles.btn}>
          {selectedLabels || placeholder || ''}
          <Collapsible.Icon
            style={{ position: 'absolute', right: 10, top: 8 }}
            open={open}
          />
        </Button>
        <Collapsible
          open={open}
          className={collapsibleClasses}
          maxHeight={maxHeight}
        >
          {
            React.Children.map(
              children, (child) => React.cloneElement(child, childProps)
            )
          }
        </Collapsible>
      </div>
    );
  }
}

MultiSelect.propTypes = {
  children: PropTypes.node.isRequired,
  label: PropTypes.string,
  placeholder: PropTypes.string,
  open: PropTypes.bool,
  onToggle: PropTypes.func,
  onBeforeMount: PropTypes.func,
  onBeforeUnmount: PropTypes.func,
  onBeforeChange: PropTypes.func,
  onAfterChange: PropTypes.func,
  onChange: PropTypes.func,
  defaultSelected: PropTypes.array, // eslint-disable-line react/forbid-prop-types
  maxHeight: PropTypes.string,
  inline: PropTypes.bool
};

MultiSelect.defaultProps = {
  label: undefined,
  placeholder: undefined,
  open: false,
  onToggle: undefined,
  onBeforeMount: undefined,
  onBeforeUnmount: undefined,
  onBeforeChange: undefined,
  onAfterChange: undefined,
  onChange: undefined,
  defaultSelected: [],
  maxHeight: undefined,
  inline: false
};

const enhancedMultiSelect = enhanceWithClickOutside(MultiSelect);

enhancedMultiSelect.Group = MultiSelectGroup;

export default enhancedMultiSelect;
