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 updateObject from "updeep/dist/update";
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,
        withDefaults: pass,
    };
}


const pass = o => o;
const plain = {};
const withSlash = s => s ? ("/" + s) : "";

export function thunk(cb) {
    return {_thunk: cb};
}
export function connectLean(options=plain) {
    return connectAdvanced(dispatch => {
        const withDefaults = s => ({...options.defaultProps, ...s});

        var boundHandlersCache = null;
        var propsCache = null;
        var prevScope = {}; // Just some object with unique identity
        const getProps = () => propsCache;

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

        const handlers = typeof options.handlers === "function"
            ? options.handlers(getProps)
            : options.handlers;

        return (fullState, ownProps) => {
            var scope = ownProps.scope || options.scope;
            if (Array.isArray(scope)) {
                scope = flattenDeep(scope);
            }

            var scopedState = fullState || plain;

            if (scope) {
                scopedState = {...getOr(plain, disableLodashPath(scope), fullState), scope};
            }

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


            // Regenerate handlers only when the scope changes
            if (prevScope !== scope) {
                prevScope = scope;
                const dispatchUpdate = (updateName, update) => {
                    if (!update) {
                        return;
                    }

                    if (update && typeof update._thunk === "function") {
                        return update._thunk(dispatchUpdate.bind(null, updateName), getProps);
                    }
                    var actionSuffix = scope;
                    if (Array.isArray(actionSuffix)) {
                        actionSuffix = actionSuffix.join(".");
                    }

                    dispatch({
                        type: "LEAN_UPDATE" + withSlash(actionSuffix) + withSlash(updateName),
                        update,
                        withDefaults,
                        scope,
                    });
                };

                const bindDispatch = (updateFn, updateName) => (...args) => dispatchUpdate(updateName, updateFn(...args.concat(propsCache)));

                boundHandlersCache = mapValuesWithKey(bindDispatch, handlers);
            }

            return propsCache = {...ownProps, ...scopedState, ...boundHandlersCache, scope};
        };

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


const actionPattern = /^LEAN_UPDATE/;

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

    if (scope) {
        return updateIn(disableLodashPath(scope), s => updateObject(update, withDefaults(s)), state);
    }

    return updateObject(action.update, withDefaults(state));
}

export default leanReducer;

@@ -974,63 +974,14 @@
     
-    withDefaults: pass,%0A    %7D;%0A%7D%0A%0A%0Aconst pass = o =%3E o;
+%7D;%0A%7D%0A%0A
 %0Acon
@@ -1198,33 +1198,23 @@
 nst 
-withDefaults = s =%3E (%7B...
+initialState = 
 opti
@@ -1233,16 +1233,17 @@
 rops
-, ...s%7D)
+ %7C%7C plain
 ;%0A%0A 
@@ -2094,32 +2094,94 @@
 %0A            %7D%0A%0A
+            scopedState = %7B...initialState, ...scopedState%7D;%0A%0A
             // I
@@ -2341,37 +2341,24 @@
 =  mapState(
-withDefaults(
 scopedState)
@@ -2348,33 +2348,32 @@
 tate(scopedState
-)
 , ownProps);%0A   
@@ -2606,29 +2606,16 @@
 apState(
-withDefaults(
 scopedSt
@@ -2617,17 +2617,16 @@
 pedState
-)
 , ownPro
@@ -3531,28 +3531,28 @@
         
-withDefaults
+initialState
 ,%0A      
@@ -4224,28 +4224,28 @@
 update, 
-withDefaults
+initialState
 %7D = acti
@@ -4290,16 +4290,29 @@
 pdateIn(
+%0A            
 disableL
@@ -4328,16 +4328,28 @@
 (scope),
+%0A           
  s =%3E up
@@ -4371,31 +4371,60 @@
 te, 
-withDefaults(s)), state
+%7B...initialState, ...s%7D),%0A            state%0A        
 );%0A 
@@ -4472,27 +4472,35 @@
 te, 
-withDefaults(
+%7B...initialState, ...
 state
-)
+%7D
 );%0A%7D

Remove weird withDefaults