import classNames from 'classnames';
import isEqual from 'lodash/isEqual';
import React from 'react';
import PropTypes from 'prop-types';

import Autocomplete from '../Autocomplete';
import BaseSelect from '../BaseSelect';
import ClearAll from '../ClearAll';
import wrap from '../ItemContainerWrapper';
import optionsFactory from './options';
import { itemHandlerCreator } from '../../utils';
import { ADD_CHICKLET, USER_SPECIFIED_OPTION, ANY } from '../../constants';

const defaultProps = {
  selectBehavior: ADD_CHICKLET,
  isDisabled: false,
  mainSelector: {
    visible: true,
    notSelectedOption: { label: 'Any' },
    userSpecifiedOption: { label: 'Search...' },
    optionGroups: [],
  },
  chicklets: {
    sortable: false,
    visible: true,
    fullwidth: false,
    buttons: {
      ascDesc: { visible: false },
      info: { visible: false },
    },
    events: {},
    clearAll: { visible: true },
  },
  events: {},
  autoComplete: {
    clearAfterSelection: true,
    renderClearTextboxButton: true,
  },
};

const propTypes = {
  kdFilterModel: PropTypes.object.isRequired,
  events: PropTypes.shape({
    onSelect: PropTypes.func,
    onChange: PropTypes.func,
  }),
  mainSelector: PropTypes.shape({
    visible: PropTypes.bool,
    notSelectedOption: PropTypes.shape({
      label: PropTypes.string,
      selected: PropTypes.bool,
    }),
    userSpecifiedOption: PropTypes.shape({
      label: PropTypes.string,
      selected: PropTypes.bool,
    }),
    optionGroups: PropTypes.arrayOf(
      PropTypes.shape({
        label: PropTypes.string,
        options: PropTypes.arrayOf(
          PropTypes.shape({
            label: PropTypes.string,
            values: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.object])),
            selected: PropTypes.bool,
          })
        ),
      })
    ),
  }),

  autoComplete: PropTypes.shape({
    url: PropTypes.string,
    key: PropTypes.string,
    bearerToken: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
    minLength: PropTypes.number,
    delay: PropTypes.number,
    placeholderText: PropTypes.string,
    filters: PropTypes.arrayOf(PropTypes.object),
    relatedEntities: PropTypes.arrayOf(PropTypes.string),
    clearAfterSelection: PropTypes.bool,
    events: PropTypes.shape({
      onSuccess: PropTypes.func,
      onRender: PropTypes.func,
      onSelect: PropTypes.func,
      onFail: PropTypes.func,
      onDone: PropTypes.func,
      valueModifier: PropTypes.func,
      beforeSend: PropTypes.func,
      onFocus: PropTypes.func,
      onBlur: PropTypes.func,
      onLoadingStart: PropTypes.func,
      onLoadingEnd: PropTypes.func,
    }),

    infoBar: PropTypes.shape({
      visible: PropTypes.bool,
      leftLabel: PropTypes.string,
      rightLabel: PropTypes.string,
    }),

    // Note: deprecated?
    search: PropTypes.shape({
      url: PropTypes.func,
      data: PropTypes.array,
    }),
  }),

  // private props
  theme: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
  selectBehavior: PropTypes.string,
  selectedValuesInitial: PropTypes.array,
  onOptionSelected: PropTypes.func.isRequired,
};

export class KdSelectEntity extends React.Component {
  constructor(props) {
    super(props);
    this.mainSelector = props.mainSelector;
    this.options = this.propBasedOptions();

    this.kdFilterOptions = {
      model: props.kdFilterModel,
      operator: 'Eq',
      itemHandler: itemHandlerCreator(props.kdFilterModel.getDataType()),
    };

    this.state = {
      selectedOption: this.options[0] || { id: null },
      selectedValue: '',
      fireEvents: true,
    };
  }

  getDefaultSelected = () => {
    const selected = this.options.filter(o => o.isSelected)[0];
    return selected;
  };

  selectDefaultOption = () => {
    if (this.options[0]) {
      this.setState({ selectedOption: this.options[0] }, () =>
        this.props.onOptionSelected(this.state.selectedOption.id)
      );
    }
  };

  componentDidMount() {
    const { selectedValuesInitial } = this.props;
    if (selectedValuesInitial.length) {
      this.setSelectedValues(this.props.selectedValuesInitial);
    } else {
      const selectedOption = this.getDefaultSelected();
      if (selectedOption) {
        this.onSelectChange({ target: { value: selectedOption.id } }, true);
      }
    }
  }

  // componentWillReceiveProps(nextProps) {
  //   const { selectedValuesInitial, onClearChicklets, kdFilterModel } = this.props;
  //   const filterHasChanged = (kdFilterModel !== nextProps.kdFilterModel);
  //   const valuesHasChanged = selectedValuesInitial
  //     && !isequal(selectedValuesInitial, nextProps.selectedValuesInitial);
  //   if (filterHasChanged || valuesHasChanged) {
  //     onClearChicklets();
  //     this.kdFilterOptions = {
  //       model: nextProps.kdFilterModel,
  //       operator: 'Eq',
  //       itemHandler: itemHandlerCreator(this.props.kdFilterModel.getDataType()),
  //     };
  //     this.setSelectedValues(nextProps.selectedValuesInitial);
  //   }
  // }

  componentWillReceiveProps(nextProps) {
    if (!isEqual(nextProps.mainSelector, this.mainSelector)) {
      this.mainSelector = nextProps.mainSelector;
      this.options = this.propBasedOptions();
      const selectedOption = this.getDefaultSelected();
      if (selectedOption) {
        this.onSelectChange({ target: { value: selectedOption.id } }, true);
      }
    }
  }

  setSelectedValues = (selectedValues) => {
    selectedValues.forEach(({ value, label }) => {
      const { userSpecifiedOption } = this.mainSelector;
      let relatedOption;
      if (this.mainSelector.visible) {
        let inputVisible = false;
        relatedOption = this.options.find(option => option.key === value);
        if (!relatedOption && userSpecifiedOption) {
          relatedOption = this.options.find(option => option.key === USER_SPECIFIED_OPTION);
          inputVisible = true;
        }

        if (relatedOption) {
          relatedOption.selected = { values: [value], label, option: value };
          this.addItem({ values: [{ value, label }], inputVisible, silent: true });
          this.setState(
            {
              selectedOption: relatedOption,
              selectedValue: relatedOption.selectedLabel,
            },
            () => this.props.onOptionSelected(this.state.selectedOption.id)
          );
        }
      } else {
        this.addItem({ values: [{ value, label }], inputVisible: true, silent: true });
      }
    });
  };

  propBasedOptions = () => {
    const { kdFilterModel } = this.props;
    const { notSelectedOption, userSpecifiedOption, optionGroups } = this.mainSelector;
    let customOptions = [];

    if (!this.mainSelector.visible) {
      return [optionsFactory.create(USER_SPECIFIED_OPTION)];
    }

    if (optionGroups.length) {
      customOptions = optionGroups[0].options.map(
        option => option.values && optionsFactory.createCustom(option)
      );
    }

    if (notSelectedOption) {
      optionsFactory.setAnyDisplayName(
        notSelectedOption.label || `Any ${kdFilterModel.getEntityType()}`
      );
      customOptions = [optionsFactory.create(ANY, notSelectedOption.selected), ...customOptions];
    }

    if (userSpecifiedOption) {
      optionsFactory.setUserSpecifiedDisplayName(
        userSpecifiedOption.label || `Search ${kdFilterModel.getEntityType()}(s)...`
      );
      customOptions = [
        ...customOptions,
        optionsFactory.create(USER_SPECIFIED_OPTION, userSpecifiedOption.selected),
      ];
    }

    return customOptions;
  };

  isInputVisible = () => this.state.selectedOption.id === USER_SPECIFIED_OPTION;

  onSelectChange = ({ target }, silent) => {
    const { events: { onSelect }, onClearChicklets } = this.props;
    const selectedOption = this.options.find(option => option.id === target.value);

    if (!selectedOption) {
      return;
    }

    this.setState(
      {
        selectedOption,
        selectedValue: selectedOption.selectedLabel,
      },
      () => {
        this.props.onOptionSelected(this.state.selectedOption.id);
        onClearChicklets();
        if (selectedOption.loadDefaultValue()) {
          const callback = () => {
            const data = {
              values: selectedOption.selected.values,
              label: selectedOption.selectedLabel,
              model: this.kdFilterOptions.model,
              ...this.kdFilterOptions.model.getFilterItemValue(),
            };
            onSelect && onSelect({ data });
          };
          this.addItem({ ...selectedOption.selected, silent }, !silent && callback);
          this.Autocomplete.setInputValue();
        }
      }
    );
  };

  addItem = ({ values, inputVisible = null, silent = false }, callback) => {
    const { events: { onChange }, autoComplete: { clearAfterSelection } } = this.props;
    values.forEach(({ value, label }) => {
      const remove = (items) => {
        if (!items.length) {
          this.selectDefaultOption();
        }
        if (this.props.isChickletNone && this.Autocomplete) {
          this.Autocomplete.setInputValue.bind(this.Autocomplete)();
        }
      };
      const event = () => {
        if (this.state.fireEvents) {
          const data = {
            value,
            label,
            model: this.kdFilterOptions.model,
            ...this.kdFilterOptions.model.getFilterItemValue(),
          };
          onChange && onChange({ data });
        }
        callback && callback();
      };
      if (!inputVisible) {
        clearAfterSelection && this.Autocomplete.setInputValue();
      }
      this.props.onItemAdded(
        {
          value,
          remove,
          chickletLabel: label,
          option: value,
          inputVisible: inputVisible || this.isInputVisible(),
          kdFilterOptions: this.kdFilterOptions,
          kdFilterModel: this.kdFilterOptions.model,
        },
        !silent && event
      );
    });
  };

  optionHelper() {
    const {
      theme,
      kdFilterModel,
      api,
      autoComplete,
      isChickletNone,
      search,
      isEntityMultiple,
      isEntityMultipleWithSelector,
      events,
      chicklets,
      emptyItems,
    } = this.props;
    return (
      <div
        className={classNames(
          { 'u-hidden': !this.isInputVisible() },
          { [theme['u-hidden']]: !this.isInputVisible() }
        )}
      >
        <Autocomplete
          theme={theme}
          ref={ref => (this.Autocomplete = ref)}
          setSelectedItems={this.addItem}
          handleRemove={this.handleRemove}
          mainSelector={this.mainSelector}
          hasInitialValues={this.props.hasInitialValues()}
          lastInitialValue={this.props.lastInitialValue()}
          {...{
            autoComplete,
            api,
            emptyItems,
            kdFilterModel,
            isChickletNone,
            search,
            isEntityMultiple,
            isEntityMultipleWithSelector,
            events,
            chicklets,
          }}
        />
      </div>
    );
  }

  render() {
    const { theme, emptyItems, isChickletNone, chicklets, onClearChicklets } = this.props;
    return (
      <div
        className={classNames(
          { 'u-disabled': this.props.isDisabled },
          { [theme['u-disabled']]: this.props.isDisabled }
        )}
      >
        {this.props.mainSelector.visible && (
          <BaseSelect
            theme={theme}
            options={this.options}
            onChange={this.onSelectChange}
            defaultValue={this.state.selectedOption.id}
          />
        )}
        {this.optionHelper()}
        {!emptyItems &&
          !isChickletNone &&
          chicklets.clearAll.visible && (
            <ClearAll theme={theme} onClick={this.props.onClearChicklets} />
          )}
      </div>
    );
  }
}

KdSelectEntity.defaultProps = defaultProps;
KdSelectEntity.propTypes = propTypes;

export default wrap(KdSelectEntity);
export const UnwrappedKdSelectEntity = KdSelectEntity;
