/* eslint-disable no-underscore-dangle */

import PropTypes from 'prop-types';
import React from 'react';

const PropTypesMap = new Map();

Object.keys(PropTypes).forEach(typeName => {
  const type = PropTypes[typeName];

  PropTypesMap.set(type, typeName);
  PropTypesMap.set(type.isRequired, typeName);
});

const isNotEmpty = obj => obj && obj.props && Object.keys(obj.props).length > 0;

const hasDocgen = type => isNotEmpty(type.__docgenInfo);

const propsFromDocgen = type => {
  const props = {};
  const docgenInfoProps = type.__docgenInfo.props;

  Object.keys(docgenInfoProps).forEach(property => {
    const docgenInfoProp = docgenInfoProps[property];
    const defaultValueDesc = docgenInfoProp.defaultValue || {};
    const propType = docgenInfoProp.flowType || docgenInfoProp.type || 'other';

    props[property] = {
      property,
      propType,
      required: docgenInfoProp.required,
      description: docgenInfoProp.description,
      defaultValue: defaultValueDesc.value,
    };
  });

  return props;
};

const propsFromPropTypes = type => {
  const props = {};

  if (type.propTypes) {
    Object.keys(type.propTypes).forEach(property => {
      const typeInfo = type.propTypes[property];
      const required = typeInfo.isRequired === undefined;
      const docgenInfo =
        type.__docgenInfo && type.__docgenInfo.props && type.__docgenInfo.props[property];
      const description = docgenInfo ? docgenInfo.description : null;
      let propType = PropTypesMap.get(typeInfo) || 'other';

      if (propType === 'other') {
        if (docgenInfo && docgenInfo.type) {
          propType = docgenInfo.type.name;
        }
      }

      props[property] = { property, propType, required, description };
    });
  }

  if (type.defaultProps) {
    Object.keys(type.defaultProps).forEach(property => {
      const value = type.defaultProps[property];

      if (value === undefined) {
        return;
      }

      // eslint-disable-next-line react/destructuring-assignment
      if (!props[property]) {
        props[property] = { property };
      }

      // eslint-disable-next-line react/destructuring-assignment
      props[property].defaultValue = value;
    });
  }

  return props;
};

export default function makeTableComponent(Component) {
  return props => {
    if (!props.type) { // eslint-disable-line
      return null;
    }

    /* eslint-disable react/destructuring-assignment */
    const propDefinitionsMap = hasDocgen(props.type)
      ? propsFromDocgen(props.type)
      : propsFromPropTypes(props.type);
    const propDefinitions = Object.values(propDefinitionsMap);
    /* eslint-enable react/destructuring-assignment */

    return <Component propDefinitions={propDefinitions} {...props} />;
  };
}
