import PropTypes from 'prop-types';
import { omit, pick, without } from 'ramda';
import React from 'react';

// We can't import Item from '.' as this would create a dependency cycle
// createItemComponent -> index -> ItemContent -> createItemComponent
import DefaultItem from './Item';
import { getDisplayName } from '../../lib';

/**
 * Standard propTypes for use by components wrapped using `createItemComponent`
 */
export const propTypes = {
  /** ID of post or term to load. */
  id: PropTypes.number,

  /** Slug of post or term to load. */
  itemSlug: PropTypes.string,

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

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

/**
 * Higher-order component that converts others to load an item from StoryShare
 * @param {Array<string>} forwardedProps - Props to forward to the component.
 * @returns {function} to wrap component
 */
const createItemComponent = (forwardedProps = []) => (WrappedComponent) => {
  /**
   * Interface for item component
   *
   * The wrapped component should:
   *  - provide a new value for `item` with the properties your component needs
   *  - add any additional props that your component needs
   */
  const itemComponentPropTypes = {
    // Copy over `propTypes` from the wrapped component
    ...WrappedComponent.propTypes,

    // ... override the `item` prop to remove `isRequired` as we will provide it
    item: PropTypes.object,
  };

  /**
   * Component that can load an item from StoryShare
   * Additional props will be passed through to the wrapped component
   * @param {Object} props - React component props.
   * @returns {element} JSX to render
   */
  const ItemComponent = (props) => {
    const requestProps = ['id', 'itemSlug', 'placeholder', 'typeSlug'];
    const omittedProps = without(forwardedProps, requestProps);

    if (props.item) {
      return <WrappedComponent { ...omit(omittedProps, props) } />;
    }

    return (
      <DefaultItem { ...pick(requestProps, props) }>
        { (loadedItem) => (
          <WrappedComponent
            item={ loadedItem }
            { ...omit(omittedProps, props) }
          />
        ) }
      </DefaultItem>
    );
  };

  ItemComponent.displayName = getDisplayName(WrappedComponent);
  ItemComponent.propTypes = itemComponentPropTypes;

  return ItemComponent;
};

export default createItemComponent;
