// Path: nlu-core/src/main/java/com/conversationkit/model/IConversationIntent.java
// public interface IConversationIntent {
//     /**
//      * @return unique ID for the intent.
//      */
//     public String getIntentId();
//     /**
//      * Slots are parameters collected from the user's input while detecting intent. As an example, a
//      * a slot might be a color. So in the user input, "Press the red button" the intent
//      * might be <code>PRESS_BUTTON</code> with a slot of <code>color</code> set to <code>red</code>.
//      * @return map of slot keys to slot values
//      */
//     public Map<String,Object> getSlots();
//     
//     public boolean getAllRequiredSlotsFilled();
// }
// 
// Path: conversation-kit/src/main/java/com/conversationkit/model/IConversationNode.java
// public interface IConversationNode<E extends IConversationEdge> {
// 
//     /**
//      * Returns a list of outbound edges from the current node. One matching 
//      * edge may be chosen to continue the conversation to the next node.
//      * @return outbound edges
//      */
//     public Iterable<E> getEdges();
// 
//     /**
//      * Adds an edge to the list of possible outbound edges.
//      * @param edge edge to add
//      */
//     public void addEdge(E edge);
// 
//     /**
//      * Returns the unique identifier for this node.
//      * @return the node id
//      */
//     public int getId();
//     
//     /**
//      * Node metadata is any additional information the node may
//      * need to build platform-specific implementations of itself. The values stored
//      * in the metadata will be highly dependent on the final use case and is
//      * designed to be highly flexible. 
//      * @return JSON metadata
//      */
//     public JsonObject getMetadata();
//     
// }
// 
// Path: redux/src/main/java/com/conversationkit/redux/Action.java
// @FunctionalInterface
// public interface Action {
//     public String getType();
// }
// 
// Path: redux/src/main/java/com/conversationkit/redux/Reducer.java
// @FunctionalInterface
// /**
//  * Reducers calculate a new state given the previous state and an action.
//  * @see <a href="https://redux.js.org/glossary#reducer">https://redux.js.org/glossary#reducer</a>
//  */
// public interface Reducer {
// 
//     Map reduce(Action action, Map currentState);
// }


import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.conversationkit.model.IConversationIntent;
import com.conversationkit.model.IConversationNode;
import com.conversationkit.redux.Action;
import com.conversationkit.redux.Reducer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

    @Override
    public Map reduce(Action action, Map currentState) {
        Map<String, Object> nextState = new HashMap(currentState);
        if (action instanceof ConversationAction) {
            ConversationAction conversationAction = (ConversationAction) action;
            switch (conversationAction.getActionType()) {
                case MESSAGE_RECEIVED:
                    nextState.remove("intentId");
                    nextState.remove("edgeId");
                    return nextState;
                case SET_NODE_ID:
                    nextState.remove("intentId");
                    nextState.remove("edgeId");
                    nextState.remove("misunderstoodCount");
                    nextState.put("nodeId", ((ConversationAction<String>) action).getPayload().get());
                    return nextState;
                case INTENT_UNDERSTANDING_SUCCEEDED:
                    nextState.remove("misunderstoodCount");
                    IConversationIntent successfulIntent = ((ConversationAction<IConversationIntent>) action).getPayload().get();
                    nextState.put("intentId", successfulIntent.getIntentId());
                    for (Map.Entry<String, Object> entry : successfulIntent.getSlots().entrySet()) {
                        if (reservedKeys.contains(entry.getKey())) {
                            logger.log(Level.WARNING, "Slot name {0} is reserved for conversation-kit internal functionality and may have unexpected consequences.", entry.getKey());
                        }
                        nextState.put(entry.getKey(), entry.getValue());
                    }
                    return nextState;
                case INTENT_UNDERSTANDING_PARTIAL:
                    nextState.remove("misunderstoodCount");
// Path: nlu-core/src/main/java/com/conversationkit/model/IConversationIntent.java
// public interface IConversationIntent {
//     /**
//      * @return unique ID for the intent.
//      */
//     public String getIntentId();
//     /**
//      * Slots are parameters collected from the user's input while detecting intent. As an example, a
//      * a slot might be a color. So in the user input, "Press the red button" the intent
//      * might be <code>PRESS_BUTTON</code> with a slot of <code>color</code> set to <code>red</code>.
//      * @return map of slot keys to slot values
//      */
//     public Map<String,Object> getSlots();
//     
//     public boolean getAllRequiredSlotsFilled();
// }
// 
// Path: conversation-kit/src/main/java/com/conversationkit/model/IConversationNode.java
// public interface IConversationNode<E extends IConversationEdge> {
// 
//     /**
//      * Returns a list of outbound edges from the current node. One matching 
//      * edge may be chosen to continue the conversation to the next node.
//      * @return outbound edges
//      */
//     public Iterable<E> getEdges();
// 
//     /**
//      * Adds an edge to the list of possible outbound edges.
//      * @param edge edge to add
//      */
//     public void addEdge(E edge);
// 
//     /**
//      * Returns the unique identifier for this node.
//      * @return the node id
//      */
//     public int getId();
//     
//     /**
//      * Node metadata is any additional information the node may
//      * need to build platform-specific implementations of itself. The values stored
//      * in the metadata will be highly dependent on the final use case and is
//      * designed to be highly flexible. 
//      * @return JSON metadata
//      */
//     public JsonObject getMetadata();
//     
// }
// 
// Path: redux/src/main/java/com/conversationkit/redux/Action.java
// @FunctionalInterface
// public interface Action {
//     public String getType();
// }
// 
// Path: redux/src/main/java/com/conversationkit/redux/Reducer.java
// @FunctionalInterface
// /**
//  * Reducers calculate a new state given the previous state and an action.
//  * @see <a href="https://redux.js.org/glossary#reducer">https://redux.js.org/glossary#reducer</a>
//  */
// public interface Reducer {
// 
//     Map reduce(Action action, Map currentState);
// }


// Path: conversation-kit/src/main/java/com/conversationkit/impl/ConversationReducer.java
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.conversationkit.model.IConversationIntent;
import com.conversationkit.model.IConversationNode;
import com.conversationkit.redux.Action;
import com.conversationkit.redux.Reducer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;


    @Override
    public Map reduce(Action action, Map currentState) {
        Map<String, Object> nextState = new HashMap(currentState);
        if (action instanceof ConversationAction) {
            ConversationAction conversationAction = (ConversationAction) action;
            switch (conversationAction.getActionType()) {
                case MESSAGE_RECEIVED:
                    nextState.remove("intentId");
                    nextState.remove("edgeId");
                    return nextState;
                case SET_NODE_ID:
                    nextState.remove("intentId");
                    nextState.remove("edgeId");
                    nextState.remove("misunderstoodCount");
                    nextState.put("nodeId", ((ConversationAction<String>) action).getPayload().get());
                    return nextState;
                case INTENT_UNDERSTANDING_SUCCEEDED:
                    nextState.remove("misunderstoodCount");
                    IConversationIntent successfulIntent = ((ConversationAction<IConversationIntent>) action).getPayload().get();
                    nextState.put("intentId", successfulIntent.getIntentId());
                    for (Map.Entry<String, Object> entry : successfulIntent.getSlots().entrySet()) {
                        if (reservedKeys.contains(entry.getKey())) {
                            logger.log(Level.WARNING, "Slot name {0} is reserved for conversation-kit internal functionality and may have unexpected consequences.", entry.getKey());
                        }
                        nextState.put(entry.getKey(), entry.getValue());
                    }
                    return nextState;
                case INTENT_UNDERSTANDING_PARTIAL:
                    nextState.remove("misunderstoodCount");
                    IConversationIntent partialIntent = ((ConversationAction<IConversationIntent>) action).getPayload().get();