import React, { Component } from 'react';
import { connect as dvaConnect } from 'dva';
import startsWith from 'lodash/fp/startsWith';
import flow from 'lodash/fp/flow';
import mapKeys from 'lodash/fp/mapKeys';
import pickBy from 'lodash/fp/pickBy';
import { getApp } from '../../data/app';

const connect = (modelCreator, { app, mapStateToProps, mapDispatchToProps, mergeProps, options }) => (Comp) => {
  const { dispatchVar, namespaceVar } = (options || {});
  class StatefulComponent extends Component {

    constructor(props, context) {
      super(props, context);
      const { name, model } = modelCreator();
      this.name = name;
      this.model = model;
      const mapState = (state) => {
        const pps = mapStateToProps ? mapStateToProps(state) : {};
        pps[name] = state[model.namespace];
        pps[namespaceVar || 'namespace'] = model.namespace;
        if (state.loading) {
          pps.loading = state.loading;
          pps.loading.model = pps.loading.models[model.namespace];
          pps.loading.effect = flow(
            pickBy((v, k) => startsWith(`${model.namespace}/`, k)),
            mapKeys(k => k.slice(model.namespace.length + 1)),
          )(pps.loading.effects);
        }
        return pps;
      };
      const mapDispatch = (dispatch) => {
        const extras = mapDispatchToProps ? mapDispatchToProps(dispatch) : {};
        return {
          dispatch,
          ...extras,
          [dispatchVar || 'dispatchLocal'](action) {
            const { type, payload } = action;
            return dispatch({ type: `${model.namespace}/${type}`, payload });
          },
        };
      };
      this.Output = dvaConnect(mapState, mapDispatch, mergeProps, options)(Comp);
    }

    componentWillMount() {
      (app || this.props.app || getApp()).model(this.model);
    }
    componentWillUnmount() {
      (app || this.props.app || getApp()).unmodel(this.model.namespace);
    }

    render() {
      const { children, ...rest } = this.props;
      return (
        <this.Output {...rest}>
          { children }
        </this.Output>
      );
    }

  }
  return StatefulComponent;
};

export default connect;