import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

const ComponentContainer = React.forwardRef(
    ({ Component, ...props }, ref) => Component && <Component ref={ref} {...props} />
);

ComponentContainer.propTypes = {
    Component: PropTypes.elementType,
};

// as default behavior it puts the ownProps (received from the parent) to the first place, so it will be overwritten
// by the values from stateProps and dispatchProps, but we need the parent values (eg. wrapper in the client project)
const mergeOwnPropsLast = (stateProps, dispatchProps, ownProps) => ({
    ...stateProps,
    ...dispatchProps,
    ...ownProps,
});

export const containerConnect = (mapStateToProps, mapDispatchToProps, mergeProps = mergeOwnPropsLast) =>
    connect(mapStateToProps, mapDispatchToProps, mergeProps)(ComponentContainer);

class ConnectedProxy extends React.Component {
    static propTypes = {
        Component: PropTypes.object,
    };

    get state() {
        return (this.component && this.component.getWrappedInstance().state) || {};
    }

    set state(value) {
        // do nothing is ok for now...
    }

    get refs() {
        return (this.component && this.component.getWrappedInstance().refs) || {};
    }

    set refs(value) {
        // do nothing is ok for now...
    }

    setComponentRef = ref => {
        this.component = ref;
    };

    getProps = () => {
        // eslint-disable-next-line no-unused-vars
        const { Component, ...props } = this.props;
        return props;
    };

    render() {
        const { ConnectedComponent } = this;
        return <ConnectedComponent ref={this.setComponentRef} {...this.getProps()} />;
    }
}

export const containerConnectProxy = (
    mapStateToProps,
    mapDispatchToProps,
    mergeProps = mergeOwnPropsLast
) => {
    return class ConnectedComponentProxy extends ConnectedProxy {
        constructor(props) {
            super(props);
            const { Component } = props;
            this.ConnectedComponent = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component);
        }
    };
};

export default containerConnect;
