import { find } from 'lodash';
import PropTypes from 'prop-types';
import cx from 'classnames';
import React, { useEffect, useState } from 'react';
import { components } from 'react-select';
import Checkbox from '../Checkbox';
import StyledSelect from './StyledSelect';
import style from './MultiSelect.module.scss';

const Option = (props) => {
  const { isSelected, label, selectOption } = props;
  return (
    <div
      className={cx(style.option, isSelected && style.selected)}
      onClick={() => selectOption(props)}
    >
      <span className={style.checkboxWrapper} style={{ pointerEvents: 'none' }}>
        <Checkbox style={{ pointerEvents: 'none' }} checked={isSelected}>
          {label}
        </Checkbox>
      </span>
    </div>
  );
};

const MultiValue = ({ children, label: labelProp, grouped, ...props }) => {
  const { getValue } = props;
  const value = getValue();
  if (children === 'Select all') {
    return null;
  }

  let total = value.length;
  if (grouped && value.length > 1) {
    const firstValue = value[0].label;
    if (firstValue === 'Select all') {
      total -= 1;
      if (children !== value[1].label) {
        return null;
      }
    } else if (children !== value[0].label) {
      return null;
    }
  }

  const label = grouped && total > 1 ? `${total} ${labelProp}` : children;
  return (
    <components.MultiValue components={components} {...props}>
      {label}
    </components.MultiValue>
  );
};

const MultiSelect = ({
  selections,
  value,
  onChange,
  placeholder = 'Select items',
  error,
  width = '100%',
  label = 'options',
  grouped = true,
  closeMenuOnSelect = true,
  disabled,
  ...rest
}) => {
  const [selectedOptions, setSelectedOptions] = useState([]);

  const options = [
    { value: 'all', label: 'Select all' },
    ...selections.map((i) => ({
      value: i.id,
      label: i.label,
    })),
  ];

  useEffect(() => {
    if (value.length === selections.length) {
      return setSelectedOptions(options);
    }

    return setSelectedOptions(value.map((i) => ({ label: i.label, value: i.id })));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [value]);

  const getSelectedOptions = (givenOptions) =>
    givenOptions.reduce((result, option) => {
      const optionValue = find(selections, (i) => i.id === option.value);
      return optionValue ? result.concat(optionValue) : result;
    }, []);

  return (
    <div onClick={(e) => e.stopPropagation()}>
      <StyledSelect
        disabled={disabled}
        options={options}
        error={error}
        isMulti
        label={label}
        closeMenuOnSelect={closeMenuOnSelect}
        containerStyles={{ width }}
        placeholder={placeholder}
        noOptionsMessage={() => `No ${label} found`}
        value={selectedOptions}
        components={{
          Option,
          MultiValue: (props) => <MultiValue {...props} grouped={grouped} label={label} />,
        }}
        onChange={(_, { action, option, removedValue }) => {
          if (action === 'select-option') {
            if (!option || !option.data) {
              return;
            }
            const newSelectedOptions = [...selectedOptions, option];
            if (option.data.value === 'all' || newSelectedOptions.length === selections.length) {
              setSelectedOptions(options);
              onChange(getSelectedOptions(options));
            } else {
              setSelectedOptions(newSelectedOptions);
              onChange(getSelectedOptions(newSelectedOptions));
            }
          } else if (action === 'deselect-option') {
            if (!option || !option.data) {
              return;
            }
            let newSelectedOptions = [];
            if (option.data.value !== 'all') {
              newSelectedOptions = selectedOptions.filter(
                (i) => i.value !== option.data.value && i.value !== 'all',
              );
            }
            setSelectedOptions(newSelectedOptions);
            onChange(getSelectedOptions(newSelectedOptions));
          } else if (action === 'remove-value' || action === 'pop-value') {
            if (grouped === false && removedValue && removedValue.value) {
              const newSelectedOptions = selectedOptions.filter(
                (i) => i.value !== removedValue.value && i.value !== 'all',
              );
              setSelectedOptions(newSelectedOptions);
              onChange(getSelectedOptions(newSelectedOptions));
            } else {
              setSelectedOptions([]);
              onChange([]);
            }
          }
        }}
        {...rest}
      />
    </div>
  );
};

MultiSelect.propTypes = {
  values: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      label: PropTypes.string.isRequired,
    }),
  ),
  selected: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      label: PropTypes.string.isRequired,
    }),
  ),
  onChange: PropTypes.func,
  width: PropTypes.string,
};

MultiSelect.defaultProps = {
  values: [],
  selected: [],
  onChange: () => {},
};

export default MultiSelect;
