const hoistStatics = require("hoist-non-react-statics");
import invariant from "invariant";
import { assign } from "lodash";
import { Component, createElement } from "react";
import { shape } from "./shape";
function getDisplayName(WrappedComponent) {
    return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
/**
 * @param mapItemsToProps
 * @returns {function(any): any}
 */
export function inject(mapItemsToProps) {
    return function wrapWithConnect(WrappedComponent) {
        const connectDisplayName = `Inject(${getDisplayName(WrappedComponent)})`;
        class Connect extends Component {
            /**
             * @param props
             * @param context
             */
            constructor(props, context) {
                super(props, context);
                this.state = {
                    hasEverythingItNeeds: false
                };
                this.serviceManager = props.serviceManager || context.serviceManager;
                let hasUndefined = Object.keys(props).length === 0 && Object.keys(mapItemsToProps).length > 0 ? true : false;
                Object.keys(props).forEach(key => {
                    if (-1 === Object.keys(mapItemsToProps).indexOf(key)) {
                        hasUndefined = true;
                    }
                });
                if (hasUndefined) {
                    invariant(this.serviceManager, `Could not find "serviceManager" in either the context or ` +
                        `props of "${connectDisplayName}". ` +
                        `Either wrap the root component in a <Provider>, ` +
                        `or explicitly pass "serviceManager" as a prop to "${connectDisplayName}".`);
                }
                else {
                    this.state.hasEverythingItNeeds = true;
                }
            }
            render() {
                let toInject = {};
                if (!this.state.hasEverythingItNeeds) {
                    let data = {};
                    Object.keys(mapItemsToProps).forEach(key => {
                        data[key] = this.serviceManager.get(mapItemsToProps[key]);
                    });
                    toInject = assign({}, this.props, data);
                }
                else {
                    toInject = this.props;
                }
                return createElement(WrappedComponent, toInject);
            }
        }
        /**
         * @type {string}
         */
        Connect.displayName = connectDisplayName;
        /**
         * @type {any}
         */
        Connect.WrappedComponent = WrappedComponent;
        /**
         * @type {{serviceManager: Requireable<any>}}
         */
        Connect.contextTypes = {
            serviceManager: shape
        };
        /**
         * @type {{serviceManager: Requireable<any>}}
         */
        Connect.propTypes = {
            serviceManager: shape
        };
        return hoistStatics(Connect, WrappedComponent);
    };
}
