import { CALL_API } from 'redux-api-middleware';
import { defaults } from 'lodash';
import toBody from './toBody';

export default function apiCreator(reducers, options) {
    const apiReducers = {};
    const apiActions = {};

    for (let reducerName in reducers) {
        const { initial, reducer, actions } = reducers[reducerName];
        const prefix = `@@api/${reducerName}/`;
        const localActions = {};
        const initialState = {
            data: initial ? initial : {},
            loading: false,
            error: null
        }

        apiReducers[reducerName] = {};
        apiActions[reducerName] = {};

        let currentReducer = reducer;
        if (typeof reducer === 'function') {
            currentReducer = reducer.bind(localActions);
        }

        for (let actionName in actions) {
            const action = actions[actionName];
            const actionPrefix = prefix + actionName;

            // middleware or local action
            if (action && action.endpoint) {
                const normalized = normalizeAction(
                    actionPrefix,
                    action,
                    options
                );

                apiActions[reducerName][actionName] = (data) => {
                    if (normalized.method !== 'GET' &&
                        normalized.method !== 'HEAD') {
                        normalized.body = toBody(data);
                    }
                    return {
                        [CALL_API]: normalized
                    }
                }

                currentReducer = createReducer(
                    initialState,
                    normalized.types,
                    currentReducer
                );

            } else {
                localActions[actionName] = actionPrefix;

                if (typeof action === 'function') {
                    apiActions[reducerName][actionName] = (data) => ({
                        type: actionPrefix,
                        payload: action(data)
                    });

                } else {
                    apiActions[reducerName][actionName] = (data) => ({
                        type: actionPrefix,
                        payload: data
                    });
                }
            }
        }
        apiReducers[reducerName] = currentReducer;
    }

    return {
        reducers: apiReducers,
        actions: apiActions
    }
}

function normalizeAction(prefix, action, options) {
    const _options = defaults(options, {
        headers: null,
        rootUrl: ''
    });

    const callbacks = [
        action.request,
        action.success,
        action.failure
    ];

    return {
        endpoint: _options.rootUrl + action.endpoint,
        types: ['/REQUEST', '/SUCCESS', '/FAILURE'].map((sufix, i) => {
            const type = prefix + sufix;
            const payload = callbacks[i];

            if (payload) {
                return {
                    type,
                    payload
                }
            }
            return type;
        }),
        headers: _options.headers,
        method: action.method
    }
}

function createReducer(initialState, types, next) {
    const typeNames = types.map(x => x.type ? x.type : x);
    const isNext = typeof next === 'function';

    return (state = initialState, action) => {
        switch (action.type) {
            case typeNames[0]:
                return {
                    ...state,
                    loading: true
                }
            case typeNames[1]:
                return {
                    ...state,
                    data: action.payload,
                    loading: false
                }
            case typeNames[2]:
                return {
                    ...state,
                    error: action.payload,
                    loading: false
                }
            default:
                return isNext
                    ? next(state, action)
                    : state;
        }
    }
}
@@ -59,16 +59,38 @@
 defaults
+, clone, isPlainObject
  %7D from 
@@ -1251,241 +1251,28 @@
  = (
-data) =%3E %7B%0A                    if (normalized.method !== 'GET' &&%0A                        normalized.method !== 'HEAD') %7B%0A                        normalized.
+params, 
 body
+)
  =
- toBody(data);%0A                    %7D%0A                    return %7B%0A    
+%3E (%7B%0A
     
@@ -1299,16 +1299,24 @@
 L_API%5D: 
+addData(
 normaliz
@@ -1317,38 +1317,31 @@
 rmalized
-%0A                    %7D
+, params, body)
 %0A       
@@ -1342,32 +1342,34 @@
                %7D
+);
 %0A%0A              
@@ -3818,16 +3818,944 @@
        %7D%0A    %7D%0A%7D
+%0A%0Afunction allowBody(action, body) %7B%0A    return action.method !== 'GET'%0A        && action.method !== 'HEAD'%0A        && isPlainObject(body);%0A%7D%0A%0Afunction assign(target, source) %7B%0A    return target ? target : Object.assign(%7B%7D, source);%0A%7D%0A%0Afunction addData(action, params, query) %7B%0A    const withParams = addParams(action.endpoint, params);%0A    let copy = null;%0A%0A    if (action.endpoint !== withParams) %7B%0A        copy = assign(copy, action);%0A        copy.endpoint = withParams;%0A    %7D%0A    if (allowBody(action, query)) %7B%0A        copy = assign(copy, action);%0A        copy.body = toBody(query);%0A    %7D%0A    return copy ? copy : action;%0A%7D%0A%0Afunction addParams(url, params) %7B%0A    const keys = params && Object.keys(params);%0A%0A    if (!keys %7C%7C keys.length === 0) %7B%0A        return url;%0A    %7D%0A    return keys.reduce((url, key) =%3E %7B%0A        const regex = new RegExp(%60:$%7Bkey%7D%60, 'g');%0A        return url.replace(regex, params%5Bkey%5D);%0A    %7D, url);%0A%7D

call api actions with parameters or queries