import {connectAdvanced} from "react-redux";
import getOr from "lodash/fp/getOr";
import updateIn from "lodash/fp/update";
import mapValues from "lodash/fp/mapValues";
import pick from "lodash/fp/pick";
import flattenDeep from "lodash/fp/flattenDeep";
import isEqual from "lodash/fp/isEqual";
import isEmpty from "lodash/fp/isEmpty";
const mapValuesWithKey = mapValues.convert({cap: false});

export function composeReducers(...reducers) {
    return (state=null, action) => {
        return reducers.filter(r => typeof r === "function").reduce(
            ((state, subReducer) => subReducer(state, action)),
            state
        );
    };
}

function disableLodashPath(path) {
    if (typeof path === "string") {
        return [path];
    }

    return path;
}

export function update(...args) {
    let scope, update;

    if (args.length === 2) {
        [scope, update] = args;
    } else {
        update = args[0];
    }

    return {
        type: "LEAN_UPDATE",
        scope,
        update,
    };
}


const plain = {};

export function thunk(cb) {
    return {_thunk: cb};
}
export function connectLean(options=plain) {
    return connectAdvanced(dispatch => {
        let initialState = plain;

        if (typeof options.getInitialState === "function") {
            initialState = options.getInitialState();
        }

        var boundHandlersCache = null;
        var scopeCache = {}; // Just something which is never equals with real scopes

        var mapState = typeof options.mapState === "function"
            ? options.mapState
            : pick(Object.keys(initialState));

        const handlers = options.handlers || plain;

        const handlerContext = {};

        return (fullState, ownProps) => {

            var generateHandlers = false;
            var scope = ownProps.scope || options.scope;
            var props = {...options.defaultProps, ...ownProps, scope};

            if (Array.isArray(scope)) {
                scope = flattenDeep(scope);
            }

            if (!isEqual(scopeCache, scope)) {
                scopeCache = scope;
                generateHandlers = true;
            }

            var scopedState = fullState || plain;

            if (!isEmpty(scopeCache)) {
                scopedState = {...getOr(plain, disableLodashPath(scopeCache), fullState), scope: scopeCache};
            }

            scopedState = {...initialState, ...scopedState};
            handlerContext.state = scopedState;
            handlerContext.props = props;

            // Implement React Redux style advanced performance pattern where
            // the mapState can create the mapState function itself
            let mappedState =  mapState(scopedState, props);
            if (typeof mappedState === "function") {
                // map state generated a new mapState function. Save it
                mapState = mappedState;
                // and use it to map the state
                mappedState =  mapState(scopedState, props);
            }


            if (generateHandlers) {

                const bindDispatch = (handler, handlerName) => {
                    var type = "LEAN_UPDATE";

                    if (Array.isArray(scopeCache)) {
                        type += "/" + scopeCache.join(".");
                    } else if (typeof scopeCache === "string") {
                        type += "/" + scopeCache;
                    }

                    type += "/" + handlerName;


                    return (...args) => {
                        var localContext = {...handlerContext, ...{
                            setState(updatedState) {

                                if (typeof updatedState === "function") {
                                    updatedState = updatedState(scopedState);
                                }

                                dispatch({
                                    type,
                                    updatedState: {...scopedState, ...updatedState},
                                    scope: scopeCache,
                                });
                            },
                        }};

                        handler.apply(localContext, args);
                    };
                };

                boundHandlersCache = mapValuesWithKey(bindDispatch, handlers);
            }

            return {...props, ...mappedState, ...boundHandlersCache, scope: scopeCache};
        };

    }, {
        getDisplayName: name => "ConnectLean(" + name + ")",
    });
}


const actionPattern = /^LEAN_UPDATE/;

export function leanReducer(state, action) {
    if (!actionPattern.test(action.type)) {
        return state;
    }
    let {scope, updatedState} = action;

    if (!isEmpty(scope)) {
        return updateIn(
            disableLodashPath(scope),
            () => updatedState,
            state
        );
    }

    return updatedState;
}

export default leanReducer;

@@ -3611,205 +3611,13 @@
 date
-dState
 ) %7B%0A%0A
-                                if (typeof updatedState === %22function%22) %7B%0A                                    updatedState = updatedState(scopedState);%0A                                %7D%0A
 %0A   
@@ -3695,16 +3695,66 @@
      typ
+e,%0A                                    initialStat
 e,%0A     
@@ -3794,49 +3794,8 @@
 date
-dState: %7B...scopedState, ...updatedState%7D
 ,%0A  
@@ -4494,25 +4494,33 @@
 %7Bscope, 
-updatedSt
+initialState, upd
 ate%7D = a
@@ -4531,32 +4531,38 @@
 n;%0A%0A
-    if (!isEmpty(scope))
+%0A    const doUpdate = state =%3E
  %7B%0A 
@@ -4572,70 +4572,130 @@
     
-return updateIn(%0A            disableLodashPath(scope),
+let s =  %7B...initialState, ...state%7D;%0A%0A        if (typeof update === %22function%22) %7B%0A            update = update(s);
 %0A       
     
@@ -4694,59 +4694,167 @@
     
+ %7D%0A%0A
      
-() =%3E updatedState,%0A            state%0A        
+   return %7B...initialState, ...state, ...update%7D;%0A    %7D;%0A%0A%0A    if (!isEmpty(scope)) %7B%0A        return updateIn(disableLodashPath(scope), doUpdate,state
 );%0A 
@@ -4874,20 +4874,23 @@
 urn 
-u
+doU
 pdate
-dS
+(s
 tate
+)
 ;%0A%7D%0A

Fix fn in setState