import React, {PropTypes, Children} from 'react';
import {loadModule} from './loader';
import omit from 'lodash/object/omit';
import {LazyStatusType} from './types';

const moduleCache = {};

const LazyComponent = React.createClass({
    propTypes: {
        import: PropTypes.string.isRequired,
        onModuleLoadFailure: PropTypes.func,
        _moduleGetter: PropTypes.func.isRequired,
        children: LazyStatusType
    },

    getInitialState() {
        let loaded;

        try {
            moduleCache[this.props.import] = this._interopRequireDefault();
            loaded = true;
        } catch (err) {
            loaded = false;
        }

        return {
            loaded,
            failed: false
        };
    },

    _interopRequireDefault() {
        const obj = this.props._moduleGetter();

        // This is what Babel does for import interoperability
        return (obj && obj.__esModule) ? obj.default : obj;
    },

    componentDidMount() {
        const {onModuleLoadFailure, import: importName} = this.props;

        if (!this.state.loaded) {
            // event listener for when component first loaded
            // this is when we load lazy component
            loadModule(importName).then(() => {
                // success
                moduleCache[importName] = this._interopRequireDefault();
                this.setState({loaded: true});
            }).catch((error) => {
                // failure
                if (onModuleLoadFailure) {
                    onModuleLoadFailure(error);
                }

                this.setState({
                    loaded: true,
                    failed: true
                });
            });
        }
    },

    render() {
        const children = Children.toArray(this.props.children);

        if (!this.state.loaded) {
            return getSingleChildIfAny(children.find(isLoadingIndicator));
        }

        if (this.state.failed) {
            return getSingleChildIfAny(children.find(isFailureIndicator));
        }

        const LoadedComponent = moduleCache[this.props.import];
        const props = omit(this.props, Object.keys(LazyComponent.propTypes));
        const doneChild = children.find(isDoneIndicator);
        return (
            <LoadedComponent {...props}>
                {doneChild ? doneChild.props.children : null}
            </LoadedComponent>
        );
    }
});

function isLoadingIndicator(child) {
    return child.props.loading;
}

function isFailureIndicator(child) {
    return child.props.failed;
}

function isDoneIndicator(child) {
    return child.props.done;
}

function getSingleChildIfAny(component) {
    return (component && component.props.children) ? Children.only(component.props.children) : null;
}

export default LazyComponent;
