import PropTypes from 'prop-types';
import {
  always,
  filter,
  find,
  isEmpty,
  isNil,
  keys,
  map,
  omit,
  path,
  propEq,
} from 'ramda';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Loader, Select } from 'semantic-ui-react';

import { withSite } from '../../addons/Site';

/* Interface */
const propTypes = {
  /**
   * From react-redux `mapDispatchToProps`
   * @private
   */
  dispatch: PropTypes.func,

  /** Predicate to restrict which items show up. */
  filterPredicate: PropTypes.func,

  /**
   * Loaded posts or terms.
   * @private
   */
  items: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    name: PropTypes.string,
    title: PropTypes.shape({ rendered: PropTypes.string.isRequired }),
  }).isRequired),

  /** Content to display while awaiting data. */
  loadingPlaceholder: PropTypes.element,

  /**
   * Called when the user tries to select a value
   * `onChange(event: SyntheticEvent, data: Object, item)`
   * @param {SyntheticEvent} event - React's original SyntheticEvent.
   * @param {Object} data - All props and proposed value.
   * @param {Object} item - Selected item.
   */
  onChange: PropTypes.func,

  /** Additional parameters to include in the API request. */
  requestParams: PropTypes.object,

  /**
   * StoryShare `Site`.
   * @private
   */
  site: PropTypes.object.isRequired,

  /** Slug for post type or taxonomy from which posts or terms are drawn. */
  typeSlug: PropTypes.string.isRequired,
};

/* Defaults */
const defaultProps = {
  filterPredicate: always(true),
  loadingPlaceholder: <Loader active inline />,
  requestParams: { },
};

/**
 * Parameters for use when requesting posts or terms
 */
const DEFAULT_PARAMS = { per_page: 100 };

/**
 * Assembles all request parameters into a single object
 * @param {Object} requestParams - Extra request parameters.
 * @returns {Object} assembled parameters
 */
const assembleParams = (requestParams) => ({
  ...DEFAULT_PARAMS,
  ...requestParams,
});

/**
 * Dropdown for choosing "items" (either posts or taxonomy terms)
 * Supports up to 100 items.
 * The following props are required.
 * Additional props will be passed through to SUI's <Select> component.
 * @returns {Object} component
 */
export class ItemSelect extends Component {
  /**
   * Loads posts or terms when the component mounts
   * Note that only the first 100 posts or terms will be displayed.
   * @returns {void}
   */
  componentDidMount = () => {
    const { typeSlug, requestParams, site } = this.props;
    site.req(typeSlug)
      .read(assembleParams(requestParams));
  };

  /**
   * Handle selection change of Select
   * @param {SyntheticEvent} event - React's synthetic event.
   * @param {Object} data - All props and proposed value.
   * @returns {void}
   */
  handleChange = (event, data) => {
    const { onChange, items } = this.props;
    if (onChange) {
      const item = find(propEq('id', data.value))(items);
      onChange(event, data, item);
    }
  }

  render = () => {
    const { filterPredicate, items, loadingPlaceholder } = this.props;

    if (isNil(items) || isEmpty(items)) {
      return loadingPlaceholder;
    }

    const options = map((item) => ({
      text: path(['title', 'rendered'], item) || item.name,
      value: item.id,
    }), filter(filterPredicate, items));

    return (
      <Select
        options={ options }
        { ...omit(keys(propTypes), this.props) }
        onChange={ this.handleChange }
      />
    );
  };
}

ItemSelect.propTypes = propTypes;
ItemSelect.defaultProps = defaultProps;

export const mapStateToProps = (_, { requestParams, site, typeSlug }) => ({
  items: site.getCollectionByRoute(typeSlug, assembleParams(requestParams)),
});

// n.b. ordering: mapStateToProps needs site
export default withSite(connect(mapStateToProps)(ItemSelect));
