'use strict';

//libs
var	q = require("q");
var socket = require('socket.io');
var session = require('express-session');
//local libs
var config = require('../configLoader/Config');
var ServerConstants = require('../constants/ServerConstants');
var AccessManager = require('../../shared/access/AccessManager');
var redisSessionStore = require('../redis/RedisSessionStore');
const { createRedisClient } = require('../redis/RedisClient');
//globals
var io;
var connectionSuccess;
var redisAdapter = require('socket.io-redis');
var async = require('async');
var clients = [];

function onAuthorizeSuccess(data, accept){
	logger.info('successful connection to socket.io');
	connectionSuccess = true;
	// accept the connection
	accept(null, true);

}

function onAuthorizeFail(data, message, error, accept){
	if(error){
		throw new Error(message);
	}
	connectionSuccess = false;
	logger.error('failed connection to socket.io:', message);

	// We use this callback to log all of our failed connections.
	accept(null, false);
}

function emitSocket(socket, webSocketMessage) {
	socket.on(webSocketMessage, function(data) {
		io.sockets.emit(webSocketMessage, data);
	});
}

module.exports = {
	listen:function(server, app) {
	    var webSocketDisabled = config.get("webSocketDisabled");

        if (webSocketDisabled) {
            logger.info("Web socket has been disabled, so document pushes will not occur");
            return;
        }

        redisSessionStore(onSessionStoreFetched);

        function onSessionStoreFetched(err, sessionStore){

            if(err){
                logger.error("Web socket server failed to start because it couldn't fetch a session store.");
                return;
            }

	        logger.info("Starting web socket");

            var baseUrlPath = require('../web/BaseURLPath').basePathWithSlashPrefix;
            var socketPath = '/socket.io';
            if (baseUrlPath !== "/") {
                socketPath = baseUrlPath + socketPath;
            }

            var sessionMiddleware = session({
                store: sessionStore, // we NEED to use a sessionstore. no memorystore please
                secret: config.get('sessionSecret'), // the sessionSecret to parse the cookie
                resave: true,
                saveUninitialized: true,
                success: onAuthorizeSuccess,  // *optional* callback on success - read more below
                fail: onAuthorizeFail // *optional* callback on fail/error - read more below
            });

            var path = {path: socketPath};
            io = socket.listen(server, path);

            const pub = createRedisClient();
            const sub = createRedisClient();

            if (pub && sub) {
                try {
                    io.adapter(redisAdapter({ pubClient: pub, subClient: sub }));
                } catch(err) {
                    logger.error('failed to connect redis server : ', err);
                }
            }

            io.use(function (socket, next) {
                if (socket.request && socket.request.res){
                  sessionMiddleware(socket.request, socket.request.res, next);
                } else {
                  sessionMiddleware(socket.request, {}, next);
                }
            });

            app.use(sessionMiddleware);

            io.sockets.on('connection', function (socket) {
                clients.push(socket);

                if (socket.request.user) {
                    socket.request.user.socketId = socket.id;
                }

                socket.use(function(packet, next){
                     if (packet.length > 0) {
                        emitSocket(socket, packet[0]);
                        return next();
                     }
                     next(new Error('Not a valid socket event'));
                });

            });
        }
	},
	close: function () {
		var deferred = q.defer();

		if (io && io.sockets && io.sockets.sockets) {

			if (io && io.sockets) {
				for (var socketId in io.sockets.sockets) {
					var socket = io.sockets.sockets[socketId];
					socket.disconnect(true);
				}
			}

			deferred.resolve();
		} else {
		    // resolve the promise rather than reject it because it isn't an error to have no socket, it just means there's nothing
            // to close
			deferred.resolve('Socket/s not disconnected.');
		}

		return deferred.promise;
	},

    pushDocumentVersions:  function(docVersions, currentDocument) {
        if (!io || !io.sockets) {
            return;
        }

        if (!currentDocument) {
            logger.error("Please pass the current version of the document in the " + 
                "currentDocument parameter of pushDocumentVersions");    
            return;
        }

        const versionIds = docVersions.map(docVersion => docVersion.systemHeader.versionId);

        const filterDoc = { // This is the expected format that the client wants to receive
            versionIds : versionIds,
            documentId : currentDocument.documentId,
            systemHeader: {     
                templateId: currentDocument.systemHeader.templateId,
                keyIds : currentDocument.systemHeader.keyIds,
                createdBy: currentDocument.systemHeader.createdBy
            }
        };

        io.sockets.emit(SharedConstants.WEB_SOCKET_MESSAGE_SAVE_DOCUMENT, filterDoc);

    },

    sendPasswordResetNotification: function(userId){

      if (!io || !io.sockets) {
        return;
      }
      io.sockets.emit(SharedConstants.WEB_SOCKET_MESSAGE_PASSWORD_RESET, userId);
    },

    emit: function(key, value) {
        if (!io || !io.sockets) {
            return;
        }
        
        io.sockets.emit(key, value);
    }
};
