import assert from '../internal/assert'

import {state} from '../channels/StateWithSideEffects'

import reduxChannelName from './reduxChannelName'


function _createReduxReducerStateObservable(channel, Reducers) {

  const reducerKeys = Object.keys(Reducers)

  return AppDispatcher =>

    AppDispatcher
      .filter(x => x && x.channel === channel)
      .scan(
        (state, action) => {

          const newFullState = {}

          for(let i=0; i<reducerKeys.length; i++) {
            const reducerKey = reducerKeys[i]
            const reducer = Reducers[reducerKey]
            const newState = reducer(state[reducerKey], action.payload)

            newFullState[reducerKey] = newState
          }

note how this returns a normal JS object. That means “state” in this stream is just a normal JS object.

          return newFullState
        },

the assumption is that if you invoke the reducer with no params, you will get back the initial state.

        reducerKeys.reduce(
          (initialState, reducerKey) => Object.assign(
            initialState,
            {[reducerKey]: Reducers[reducerKey](undefined, {})}
          ),
          {}
        )
      )
}


/**
 * Creates a special channel for redux reducers. The main differences between this and
 * `createChannels` are:
 *
 * 1. `createChannels` assumes a single reducer per action type. `createReduxReducers`
 *    passes all actions to *every* reducer, as per the redux style.
 * 2. `createChannels` maps each channel to a single top-level state property.
 *    `createReduxReducers` maps every reducer to a top-level state property. But
 *     internally, all messages flow through a single redux channel.
 *
 * ## Limitations
 *
 * Currently, `redux#combineReducers` is not supported.
 *
 * ## Message Dispatching
 *
 * To pass a message to a redux reducer from an rflux app, use this format:
 * - channel:String = reduxChannelName (see ./reduxChannelName)
 * - actionType:String
 * - payload: {//actual redux action }
 *
 * @param {Map<string,Function>} Reducers
 * @returns {Function} the redux reducers channel
 */
export default function createReduxReducers({Reducers, AppDispatcher}) {

  assert(Reducers, 'Need Reducers')

  const combinedStateObservable =
    _createReduxReducerStateObservable(reduxChannelName, Reducers)(AppDispatcher)

  return Object.keys(Reducers)
    .map(reducerKey => {
      const reducerStateObservable = combinedStateObservable
        .map(combinedState => combinedState[reducerKey])

check that objects point to the same thing eslint-disable-next-line eqeqeq

        .skipDuplicates((prev, next) => prev == next)

      return {
        name: reducerKey,
        stateWithSideEffectsObservable:
          reducerStateObservable.map(reducerState => state(reducerState)),
        observable: {
          [reducerKey]: reducerStateObservable,
        },
        channel: {}
      }
    })
}
h