/*
 * Wegas
 * http://wegas.albasim.ch
 *
 * Copyright (c) 2013-2017 School of Business and Engineering Vaud, Comem
 * Licensed under the MIT License
 */
package com.wegas.core.rest.util;

import com.wegas.core.ejb.RequestFacade;
import com.wegas.core.ejb.RequestManager;
import com.wegas.core.ejb.WebsocketFacade;
import com.wegas.core.exception.client.WegasErrorMessage;
import com.wegas.core.exception.client.WegasRuntimeException;
import com.wegas.core.exception.client.WegasWrappedException;
import com.wegas.core.persistence.AbstractEntity;
import com.wegas.core.security.ejb.UserFacade;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.ejb.EJB;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Francois-Xavier Aeberhard (fx at red-agent.com)
 */
@Provider
//@Stateless
public class ManagedModeResponseFilter implements ContainerResponseFilter {

    private final static Logger logger = LoggerFactory.getLogger(ManagedModeResponseFilter.class);
    /**
     *
     */
    @EJB
    private WebsocketFacade websocketFacade;
    /**
     *
     */
    @EJB
    private RequestFacade requestFacade;

    @EJB
    private UserFacade userFacade;

    /**
     * This method encapsulates a Jersey response's entities in a ServerResponse
     * and add server side events.
     *
     * @param request
     * @param response
     */
    @Override
    public void filter(ContainerRequestContext request, ContainerResponseContext response) {
        final String managedMode = request.getHeaderString("managed-mode");

        // Todo find a way to access response from RequestManager.preDestroy (@Context HttpServletResponse?)  WHY ?
        RequestManager requestManager = requestFacade.getRequestManager();

        requestManager.markManagermentStartTime();
        requestManager.setStatus(response.getStatusInfo());

        if (response.getStatusInfo().getStatusCode() >= 400) {
            logger.warn("Problem : " + response.getEntity());
        }

        boolean rollbacked = false;

        Map<String, List<AbstractEntity>> updatedEntitiesMap = requestManager.getUpdatedEntities();
        Map<String, List<AbstractEntity>> destroyedEntitiesMap = requestManager.getDestroyedEntities();
        Map<String, List<AbstractEntity>> outdatedEntitiesMap = requestManager.getOutdatedEntities();

        boolean isManaged= managedMode != null && !managedMode.toLowerCase().equals("false");

        if (isManaged){
            ManagedResponse serverResponse = new ManagedResponse();
            /* 
             * returnd entities are not to propagate through websockets
             * unless they're registered within requestManager's updatedEntities
             */
            List updatedEntities;
            List deletedEntities = new LinkedList<>();


            /*
             * if response entity is kind of exception.
             * it means something went wrong during the process -> Rollback any db changes
             *
             * Behaviour is to return a managed response with an empty entity list
             * and to register the exception as a request exception event
             */
            if (response.getEntity() instanceof Exception || requestManager.getExceptionCounter() > 0 || response.getStatusInfo().getStatusCode() >= 400) {

                // No Entities but register exception as event
                updatedEntities = new LinkedList<>();
                WegasRuntimeException wrex;

                if (response.getEntity() instanceof WegasRuntimeException) {
                    wrex = (WegasRuntimeException) response.getEntity();
                } else if (response.getEntity() instanceof Exception) {
                    wrex = new WegasWrappedException((Exception) response.getEntity());
                } else {
                    wrex = WegasErrorMessage.error("Something went wrong");
                }
                requestManager.addException(wrex);

                // Set response http status code to 400
                if (response.getStatusInfo().getStatusCode() < 400) {
                    response.setStatus(HttpStatus.SC_BAD_REQUEST);
                }
                rollbacked = true;
            } else {
                /* 
                 * Request has been processed without throwing a fatal exception
                 * -> Include all returned entities (modified or not) in the managed response
                 */

                List entities;

                if (response.getEntity() instanceof Collection) {
                    entities = new LinkedList<>();
                    for (Object o : (Collection) response.getEntity()) {
                        entities.add(o);
                    }
                    //entities = (List<Object>) response.getEntity();
                } else if (response.getEntity() instanceof ScriptObjectMirror
                        && ((ScriptObjectMirror) response.getEntity()).isArray()) {
                    entities = new LinkedList(((ScriptObjectMirror) response.getEntity()).values());
                } else if (response.getEntity() != null) {
                    entities = new LinkedList<>();
                    entities.add(response.getEntity());
                } else {
                    entities = new LinkedList<>();
                }

                updatedEntities = entities;

                response.setStatus(HttpStatus.SC_OK);
            }
            if (!rollbacked && !(updatedEntitiesMap.isEmpty() && destroyedEntitiesMap.isEmpty() && outdatedEntitiesMap.isEmpty())) {
                /*
                 * Include all detected updated entites within updatedEntites 
                 * (the ones which will be returned to the client)
                 */
                for (Entry<String, List<AbstractEntity>> entry : updatedEntitiesMap.entrySet()) {
                    String audience = entry.getKey();
                    if (userFacade.hasPermission(audience)) {
                        for (AbstractEntity ae : entry.getValue()) {
                            if (!updatedEntities.contains(ae)) {
                                updatedEntities.add(ae);
                            }
                        }
                    }
                }
                /*
                 * Let's do the same but for destroyed entities
                 */
                for (Entry<String, List<AbstractEntity>> entry : destroyedEntitiesMap.entrySet()) {
                    String audience = entry.getKey();
                    if (userFacade.hasPermission(audience)) {
                        for (AbstractEntity ae : entry.getValue()) {
                            if (!deletedEntities.contains(ae)) {
                                deletedEntities.add(ae);
                            }
                            /*
                             * Since each entity which has been returned by the rest method is included
                             * within updatedEntities list by default, make sure to not include thoses which
                             * where destroyed
                             */
                            if (updatedEntities.contains(ae)) {
                                updatedEntities.remove(ae);
                            }
                        }
                    }
                }
            }


            if (!rollbacked && !(updatedEntitiesMap.isEmpty() && destroyedEntitiesMap.isEmpty() && outdatedEntitiesMap.isEmpty())) {
                requestManager.markPropagationStartTime();
                String socketId = requestManager.getSocketId();
                websocketFacade.onRequestCommit(updatedEntitiesMap, destroyedEntitiesMap, outdatedEntitiesMap,
                        (isManaged && socketId.matches("^[\\d\\.]+$") ? socketId : null));
                requestManager.markPropagationEndTime();
            }

            // Push events stored in RequestManager
            serverResponse.getEvents().addAll(requestManager.getClientEvents());

            // Set entities
            serverResponse.setUpdatedEntities(updatedEntities);
            serverResponse.setDeletedEntities(deletedEntities);

            response.setEntity(serverResponse);

        }
        //requestFacade.flushClear();
        requestManager.markSerialisationStartTime();
    }
}

@@ -7954,24 +7954,168 @@
 SocketId();%0A
+                if (!isManaged %7C%7C socketId == null %7C%7C !socketId.matches(%22%5E%5B%5C%5Cd%5C%5C.%5D+$%22))%7B%0A                    socketId = null;%0A                %7D%0A
             
@@ -8241,72 +8241,16 @@
     
-(isManaged && socketId.matches(%22%5E%5B%5C%5Cd%5C%5C.%5D+$%22) ? socketId : null)
+socketId
 );%0A 

Fix CI