var usersApi = {},
    common = require('./../../utils/common.js'),
	logger = require('../../../logger'),
    campaignCommon = require('./../../utils/campaign.common.js'),
    mail = require('./mail.js'),
    moment = require('moment'),
    crypto = require('crypto'),
	ErrorMsg = require('../../constants/constants.js'),
    cacheApi = require('./../../utils/semusi.cache.js'),
	validate = require('../../constants/constants'),
    async = require('async');
	global.atob = require("atob");
	semusiConfig = require('./../../config');

(function (usersApi) {

	function shaHash(str, addSalt) {
		let salt = (addSalt) ? new Date().getTime() : "";
		return crypto.createHmac(semusiConfig.shaV1Hash, salt + "").update(str + "").digest('hex');
	}
    usersApi.getCurrentUser = function (params) {
        delete params.member.password;

        common.returnOutput(params, params.member);
        return true;
    };

	usersApi.getAppUsersDetails = async function (params) {
		let adminAccess = (params.member.global_admin) ? true : params.member.user_role.admin
		if (params.member.global_admin || (adminAccess && adminAccess.includes(params.qstring.args.appid))) {
			try {
				const fetchData = require("../data/didDetails").fetchData
				let appid = params.qstring.args.app_id
				let did = params.qstring.args.did
				let data = await fetchData(appid, did)
				common.returnOutput(params, data)
			} catch (err) {
				logger.error(`error occured in getAppUsersDetails", ${err}`);
				common.returnOutput(params, {})
			}

		} else {
			common.returnMessage(params, 401, validate.getErrorMessage('UnAuthorizedUser'));
		}

	}
    usersApi.createUser = function (params) {
        /*if (!params.member.global_admin) {
            common.returnMessage(params, 401, 'User is not a global administrator');
            return false;
        }*/

        let argProps = {
                'full_name':    { 'required': true, 'type': 'String' },
                'username':     { 'required': true, 'type': 'String' },
                'password':     { 'required': true, 'type': 'String' },
                'email':        { 'required': true, 'type': 'String' },
                'title':        { 'required': true, 'type': 'String' },
                'company':      { 'required': true, 'type': 'String' },
                'userpicurl':   { 'required': true, 'type': 'String' },
                'global_admin': { 'required': false, 'type': 'Boolean' }
            },
            newMember = {};

        if (!(newMember = common.validateArgs(params.qstring.args, argProps))) {
            common.returnMessage(params, 400, 'Not enough args');
            return false;
        }

        common.db.collection('members').findOne({ $or : [ {email: newMember.email}, {username: newMember.username} ] }, function(err, member) {
            if (member || err) {
                common.returnMessage(params, 200, 'Email or username already exists');
                return false;
            } else {
                createUser();
                return true;
            }
        });

        function createUser() {
            let pNoHash = newMember.password;
            newMember.password = common.shaHash(newMember.password);
            newMember.created_at = Math.floor(((new Date()).getTime()) / 1000); //TODO: Check if UTC

            common.db.collection('members').insert(newMember, {safe: true}, function(err, member) {
                if (member && member.length && !err) {

                    member[0].api_key = common.mdALgo(member[0]._id + (new Date().getTime()));
                    let modifiedOn = moment.utc().valueOf();
                    common.db.collection('members').update({'_id': member[0]._id}, {$set: {api_key: member[0].api_key, modifiedOn:modifiedOn}});
                    mail.sendToNewMember(member[0], pNoHash);

                    delete member[0].password;

                    common.returnOutput(params, member[0]);
                } else {
                    common.returnMessage(params, 500, 'Error creating user');
                }
            });
        }

        return true;
    };

    usersApi.updateUser = function (params) {
        let argProps;
        if(params.qstring.args.password!="")
        {
         argProps = {
                'user_id':      { 'required': true, 'type': 'String', 'min-length': 24, 'max-length': 24, 'exclude-from-ret-obj': true },
                'full_name':    { 'required': false, 'type': 'String', 'regex': validate.getRegex('username') },
                'phone':        { 'required': false, 'type': 'String', 'regex': validate.getRegex('phone') },
                'title':        { 'required': false, 'type': 'String', 'regex': validate.getRegex('name') },
                'company':      { 'required': false, 'type': 'String', 'regex': validate.getRegex('company') },
                'password':     { 'required': false, 'type': 'String' },
            }
        let updatedMember = {},
            pNoHash = "";
        }
        else
        {
             argProps = {
                'user_id':      { 'required': true, 'type': 'String', 'min-length': 24, 'max-length': 24, 'exclude-from-ret-obj': true },
                'full_name':    { 'required': false, 'type': 'String', 'regex': validate.getRegex('username') },
                'phone':        { 'required': false, 'type': 'String', 'regex': validate.getRegex('phone') },
                'title':        { 'required': false, 'type': 'String', 'regex': validate.getRegex('name') },
                'company':      { 'required': false, 'type': 'String', 'regex': validate.getRegex('company') },
            },
            updatedMember = {},
            pNoHash = "";
        }
		params.qstring.args = (typeof(params.qstring.args)== 'string' ) ? JSON.parse(params.qstring.args) : params.qstring.args

        if (!(updatedMember = common.validateArgs(params.qstring.args, argProps))) {
            common.returnMessage(params, 422, 'Validation error');
            return false;
        }

        if (params.member._id === params.qstring.args.user_id) {
            common.returnMessage(params, 401, 'User is not a global administrator');
            return false;
        }

		if (updatedMember.password) {
            const decodedPass = Buffer.from(updatedMember.password, 'base64').toString('utf-8');
			let reTest = /^(?:(?=.*[a-z])(?:(?=.*[A-Z])(?=.*[\d\W])|(?=.*\W)(?=.*\d))|(?=.*\W)(?=.*[A-Z])(?=.*\d)).{8,}$/;
			if (!reTest.test(decodedPass)) {
				common.returnMessage(params, 422, 'Password must be at least 8 characters and contain at least one uppercase letter or one lowercase letter or one number or special character.');
				return false;
			}
			
			if(semusiConfig.shaV1Hash == 'sha256'){
				updatedMember.password = common.shaHash(updatedMember.password);
			}else{
				pNoHash = atob(updatedMember.password);
				updatedMember.password = common.shaHash(pNoHash);
			}
		}
        updatedMember.modifiedOn = moment.utc().valueOf();
        common.db.collection('members').update({'_id': common.db.ObjectID(params.qstring.args.user_id)}, {'$set': updatedMember}, {safe: true}, function(err, isOk) {
            common.db.collection('members').findOne({'_id': common.db.ObjectID(params.qstring.args.user_id)}, function(err, member) {
                if (member && !err) {
                    //Clear this member from redis cache as its details are updated.
                    cacheApi.deleteKey(member.api_key,function(err,res){
						logger.info(`Redis key cleared. ${member.api_key}`);    
                    });
                    if (params.qstring.args.send_notification && pNoHash) {
                        mail.sendToUpdatedMember(member, pNoHash);
                    }
                    common.returnMessage(params, 200, 'Success');
                } else {
                    common.returnMessage(params, 500, 'Error updating user');
                }
            });
        });

        return true;
    };

    usersApi.deleteUser = function (params) {
        let argProps = {
                'user_ids': { 'required': true, 'type': 'Array' }
            },
            userIds = [];

        if (!params.member.global_admin) {
            common.returnMessage(params, 401, 'User is not a global administrator');
            return false;
        }

        if (!(userIds = common.validateArgs(params.qstring.args, argProps).user_ids)) {
            common.returnMessage(params, 400, 'Not enough args');
            return false;
        }

        for (let i = 0; i < userIds.length; i++) {
            // Each user id should be 24 chars long and a user can't delete his own account
            if (userIds[i] === params.member._id || userIds[i].length !== 24) {
                continue;
            } else {
                common.db.collection('members').remove({'_id': common.db.ObjectID(userIds[i])});
            }
        }

        common.returnMessage(params, 200, 'Success');
        return true;
    };

    usersApi.recover = function (params)
    {
        let argProps = {
            'email':        { 'required': true, 'type': 'String' },
        },
        recoverMember = {};

        if (!(recoverMember = common.validateArgs(params.qstring.args, argProps)))
        {
            common.returnMessage(params, 400, 'Not enough args');
            return false;
        }

    };

    usersApi.updateApprovedUser = function (params) {

	  let adminAccess = (params.member.global_admin) ? true : params.member.user_role.admin
	  if (params.member.global_admin || (adminAccess && adminAccess.includes(params.qstring.app_id))) {
        let argProps = {
                'user_id':  { 'required': true, 'type': 'String', 'min-length': 24, 'max-length': 24, 'exclude-from-ret-obj': true },
                'isapproved': { 'required': true, 'type': 'Boolean' },
                'modified_by':  { 'required': true, 'type': 'String', 'min-length': 24, 'max-length': 24, 'exclude-from-ret-obj': true },
                'email' : { 'required': true, 'type': 'String' }

            },
			ApprovedUser = {};

             ApprovedUser.modifiedOn = moment.utc().valueOf();

			 common.db.collection('members').update(
				{ '_id': common.db.ObjectID(params.qstring.args.user_id) },
				{
				  // Use $push or $pull based on params.qstring.args.isapproved
				  [params.qstring.args.isapproved ? '$push' : '$pull']: {
					'approvedAppids': params.qstring.app_id
				  },
				  // Set other fields you want to update
				  $set: {
					'modified_by': params.qstring.args.modified_by,
					'modifiedOn': ApprovedUser.modifiedOn
				  }
				},
				{ 'upsert': true, 'safe': true },
				function(err, isOk) {
			  
                common.db.collection('members').findOne({'_id': common.db.ObjectID(params.qstring.args.user_id)}, function(err, member) {
                   if (member && !err) {
                    //    mail.sendApprovedInfo(member,params.qstring.args.isapproved);
                        //Clear this member from redis cache as its details are updated.
                        cacheApi.deleteKey(member.api_key,function(err,res){
							logger.info(`Redis key cleared. ${member.api_key}`); 
                        });
                       common.returnOutput(params, ApprovedUser);
                  }
                });
		 		}
		 	);
        return true;
	  }else{
		common.returnMessage(params, 401, ErrorMsg.getErrorMessage("UnAuthorizedUser"));

	 }
    };

    usersApi.getAllUsersNew = function (params) {

		params.qstring.args =  common.isJson(params.qstring.args)?JSON.parse(params.qstring.args):params.qstring.args;
		params.qstring.args.appid =  common.isJson(params.qstring.args.appid)?JSON.parse(params.qstring.args.appid):params.qstring.args.appid;
		let adminAcess = (params.member.global_admin) ? true : params.member.user_role.admin
		if (params.member.global_admin ) {
			  //if the current user is Global Admin or admin he gets all users
			  // Added ApprovedAppids, that we need ,in FE
			common.db.collection('members').find({$or: [{ "isdeleted": { $ne: true } },{ "isdeleted": { $exists: false } }]},{user_role:1,email:1,username:1,createdOn:1, modifiedOn:1,approvedAppids:1,}).sort({createdOn:-1}).toArray(function (err, members) {

					if (!members || err) {
						fetchAndSendConfigWithOutput(params,{})
					return false;
				}
	
				fetchAndSendConfigWithOutput(params,members)
				return true;
			});

	    }else{
				//if the user is not Global admin then he needs only users of selected app and he shouln't be global Admin 
				common.db.collection('members').find({
						// Query Should be combination so we are using AND 
						$and: [
							// 1. he should be admin , manager and maarketer 
							{
								$or: [
									{"user_role.admin": params.qstring.args.appid},
									{"user_role.manager": params.qstring.args.appid},
									{"user_role.marketer": params.qstring.args.appid}
								]
							},
							//2. Plus He shouldn't be Global_admin 
							{
								"global_admin": {$ne: true}
							},
						    // 3. isdeleted should not be true
							{
								"isdeleted": {$ne: true}
							}							
						]
					},
								  // Added ApprovedAppids, that we need ,in FE
				{user_role:1,email:1,username:1,createdOn:1, modifiedOn:1,approvedAppids:1}).sort({createdOn:-1}).toArray(function (err, members) {

						//if the user has not selected an app then appID will be Null
					if (!members || err || params.qstring.args.appid == null) {
						fetchAndSendConfigWithOutput(params,{})
					return false;
				}
 
				fetchAndSendConfigWithOutput(params,members)
				return true;
			});
	
			}
			return true;
		};


	// soft delete user in db by global admin only 
	usersApi.deleteUserpermenantly = function(params){
		if(params.member.global_admin){
			let modifiedOn = moment.utc().valueOf();
			common.db.collection('members').update({"_id": common.db.ObjectID(params.qstring.args.userId)},{$set: {"isdeleted":true,"modified_by": params.qstring.args.modified_by,"modifiedOn": modifiedOn}},{multi:true},function(err, data) {
				if( !err && data) {
	
					logger.info("user deleted successfully", data);
					//Delete member from redis as its details are updated
					cacheApi.deleteKey(params.qstring.api_key,function(err,res){
						common.returnOutput(params, data);
					});
				}
				else{
					logger.error('error in deletion', err);
					common.returnOutput(params, err);
				}
			})

			
		}else{
			common.returnMessage(params, 401, ErrorMsg.getErrorMessage("UnAuthorizedUser"));

		}
	 }
		usersApi.addUsersRole = function (params) {
			//Check if appid exist or not for specific members document
			let argProps = {

                'email' : { 'required': true, 'type': 'String' },
				'role': {'required': false , 'type':'String'},
				'appid': {'required':true,'type':'String'},
				'modified_by':  { 'required': true, 'type': 'String'}
            }
			let userInfo = {};
			params.qstring.args = (typeof(params.qstring.args)== 'string' ) ? JSON.parse(params.qstring.args) : params.qstring.args
			if (!(userInfo = common.validateArgs(params.qstring.args, argProps)))
			{
				common.returnMessage(params, 400, 'Not enough args');
				return false;
			}
			let role = userInfo.role;
			let modifiedOn = moment.utc().valueOf();
			const emailQuery =  { 'email': { $regex: new RegExp('^' + userInfo.email, 'i') } };
			common.db.collection("members").findOne(emailQuery, function (err, user) {
        	  if (user) {
				//if user has user_role key
        	    if (user.user_role) {
        	      let allRoles = Object.keys(user.user_role);

					// handle this appid if it already exist in any other role 
        	      allRoles.forEach((oneRole) => {
        	        const index = user.user_role[oneRole].indexOf(userInfo.appid);
        	        if (index > -1) {
        	          user.user_role[oneRole].splice(index, 1);
        	        }
        	      });

				  //create payload for db to update
				  
				  //if this role is present in user_role key
        	      if (user.user_role[role]) {
        	        user.user_role[role].push(userInfo.appid);
        	        updateUserRole(user.user_role);
        	      } else {
					//if this role is not present in user_role key create a array
        	        user.user_role[role] = [];
        	        user.user_role[role].push(userInfo.appid);
        	        updateUserRole(user.user_role);
        	      }
        	    } else {
					
					//if this user_role doesn't exist for this user create user_role key as per params
        	      if (role == "admin") {
        	        user_role = { admin: [userInfo.appid]};
        	        updateUserRole(user_role);
        	      }
        	      if (role == "manager") {
        	        user_role = { manager: [userInfo.appid]};
        	        updateUserRole(user_role);
        	      }
        	      if (role == "marketer") {
        	        user_role = {marketer: [userInfo.appid]};
        	        updateUserRole(user_role);
        	      }
        	    }

			    /**
			     * update User_role in the user Document 
			     * @param user_role key to be updated
			     * @return 
			     **/ 
        	    function updateUserRole(user_role) {
				const emailQuery =  { 'email': { $regex: new RegExp('^' + userInfo.email, 'i') } };
        	      common.db.collection("members").update(emailQuery,{$set: {user_role: user_role,modified_by: params.qstring.args.modified_by,modifiedOn: modifiedOn,},},function (err, data) {
        	            if (!err || data) {
        	              logger.info(`User added as ${role}`);
        	              common.returnOutput(params, data);
        	            } else {
        	              logger.error(`error while added user as${role} ==>> ${err}`);
        	              common.returnOutput(params, err);
        	            }
        	          }
        	        );
        	    }
        	  }
        	});		
		}
	
		usersApi.validateEmail = function( params) {
			const emailQuery =  { 'email': { $regex: new RegExp('^' + params.qstring.args.email, 'i') } };
			common.db.collection('members').findOne(emailQuery, function(err ,data) {
				if(err || !data) {
					common.returnMessage(params, 200, 'User not Found');
				}else{
					common.returnOutput(params, data);
				}
	
			});
	}
	
	
		
	usersApi.updateRole = function (params) {
		let adminAcess = (params.member.global_admin) ? true : params.member.user_role.admin
		let modifiedOn = moment.utc().valueOf();
			if(params.member.global_admin || adminAcess.includes(params.qstring.args.appid) ){	
			common.db.collection('members').update({"_id": common.db.ObjectID(params.qstring.args.userId)}, {$pull:{"user_role.admin":{$in:[params.qstring.args.appid]},"user_role.manager":{$in:[params.qstring.args.appid]},"user_role.marketer":{$in:[params.qstring.args.appid]}}},{multi:true},function(err, data) {
				if(!err && data) {
					let role = params.qstring.args.role;
					if(role == 'admin') {
						common.db.collection('members').update({"_id": common.db.ObjectID(params.qstring.args.userId)}, {$push:{"user_role.admin":params.qstring.args.appid},$set: {"modified_by": params.qstring.args.modified_by,"modifiedOn": modifiedOn}}, function(err, data) {
							if(!err || data) {
								common.returnOutput(params, data);
							}
							else{
								common.returnOutput(params, err);
							}
						})
					}
					if(role == 'marketer') {
						common.db.collection('members').update({"_id": common.db.ObjectID(params.qstring.args.userId)}, {$push:{"user_role.marketer":params.qstring.args.appid},$set: {"modified_by": params.qstring.args.userId,"modifiedOn": modifiedOn}}, function(err, data) {
							if(!err || data) {
								common.returnOutput(params, data);
							}
							else{
								common.returnOutput(params, err);
							}
						})
					}
	
					if(role == 'manager') {
						common.db.collection('members').update({"_id": common.db.ObjectID(params.qstring.args.userId)}, {$push:{"user_role.manager":params.qstring.args.appid},$set: {"modified_by": params.qstring.args.userId,"modifiedOn": modifiedOn}}, function(err, data) {
							if(!err || data) {
								common.returnOutput(params, data);
							}
							else{
								common.returnOutput(params, err);
							}
						})
					}
					
				
				}
				else {
					common.returnOutput(params, err);
				}
	
				
			}) 
		}else{
			common.returnOutput(params, null);
		  }
		
		}

// This Function unblocks the user that has been blocked ,In this case the user's pwd will be same 
usersApi.unblockUser = function (params) {
  let adminAccess = (params.member.global_admin) ? true : params.member.user_role.admin
  if (params.member.global_admin || (adminAccess && adminAccess.includes(params.qstring.app_id))) {	
	let modifiedOn = moment.utc().valueOf();
	params.qstring.args = common.isJson(params.qstring.args)?JSON.parse(params.qstring.args):params.qstring.args;
	const emailQuery =  { 'email': { $regex: new RegExp('^' + params.qstring.args.email, 'i') } };
	   common.db.collection('members').update(emailQuery, {$unset: {isblocked :0},$set:{"logIn_Count":"1","modified_by": params.qstring.args.modified_by,"modifiedOn": modifiedOn}},function(err, result) {

		if( !err && result) {				
			logger.info('user unblocked successfully', result);
			common.returnOutput(params,"Unblocked");			
		}
		else{
			logger.error('error in user unblock', err);
			common.returnOutput(params, err);
		}
	});
  }else{
	common.returnMessage(params, 401, ErrorMsg.getErrorMessage("UnAuthorizedUser"));

 }
}

	usersApi.resetUserPassword = function (params) {
			
	  let adminAccess = (params.member.global_admin) ? true : params.member.user_role.admin
	  if (params.member.global_admin || (adminAccess && adminAccess.includes(params.qstring.args.appid))) {	
		params.qstring.args = common.isJson(params.qstring.args)?JSON.parse(params.qstring.args):params.qstring.args;
		let Password = shaHash(params.qstring.args.field);
		let modifiedOn = moment.utc().valueOf();
		common.db.collection('members').update({"_id": common.db.ObjectID(params.qstring.args.userId)}, {$set:{"password":Password,"systemPassword":true,"modified_by": params.qstring.args.modified_by,"modifiedOn": modifiedOn}},function(err, data) {
			if( !err && data) {
				logger.info('password added successfuly', data);
				// check the type of rest pwd setting in apps collection 

				common.db.collection('apps').find({'_id': common.db.ObjectID(params.qstring.args.appid)}).toArray(function(err, appDetails) {
					if (!err && appDetails) {
						if(appDetails[0].ResetPassswordMode == undefined || appDetails[0].ResetPassswordMode == "SMTP" ){
								// by default SMTP even when there is no field set  
								// sending mail
								mail.sendUpdatedPassword(params.qstring.args.email,params.qstring.args.field)
								logger.info("Email Sent");
								common.returnOutput(params,"Email Sent");		

						}else{
							logger.info("Email Sent");
							common.returnOutput(params,"Email Sent");			
						}
					}
				});
			}
			else{
				logger.error("error while reset password", err);
				common.returnOutput(params, err);
			}
		})
	 }else{
		common.returnMessage(params, 401, ErrorMsg.getErrorMessage("UnAuthorizedUser"));
	 }
	}

	usersApi.deleteUsersRole = function (params) {
	  let adminAccess = (params.member.global_admin) ? true : params.member.user_role.admin
	  if (params.member.global_admin || (adminAccess && adminAccess.includes(params.qstring.args.appid))) {	
		let adminAcess =  (params.member.global_admin) ? true :params.member.user_role.admin

		params.qstring.args = common.isJson(params.qstring.args)?JSON.parse(params.qstring.args):params.qstring.args;

        let modifiedOn = moment.utc().valueOf();
		if(params.member.global_admin ||adminAcess.includes(params.qstring.args.appid)){
		common.db.collection('members').update({"_id": common.db.ObjectID(params.qstring.args.userId)},{$pull:{"user_role.admin":{$in:[params.qstring.args.appid]},"user_role.manager":{$in:[params.qstring.args.appid]},"user_role.marketer":{$in:[params.qstring.args.appid]}},$set: {"modified_by": params.qstring.args.modified_by,"modifiedOn": modifiedOn}},{multi:true},function(err, data) {
			if( !err && data) {

				logger.info("user deleted successfully", data);
				//Delete member from redis as its details are updated
				cacheApi.deleteKey(params.qstring.api_key,function(err,res){
					logger.info("Redis key cleared. "+params.qstring.api_key);
					common.returnOutput(params, data);
				});
			}
			else{
				logger.error('error in deletion', err);
				common.returnOutput(params, err);
			}
		})

	}else{
		common.returnOutput(params, null);
	  }
	 }else{
		common.returnMessage(params, 401, ErrorMsg.getErrorMessage("UnAuthorizedUser"));

	 }
	}
    usersApi.getUserProfile = function(params){
        let argProps = {
            'userid':        { 'required': true, 'type': 'String' },
        },
        userInfo = {};

        if (!(userInfo = common.validateArgs(params.qstring.args, argProps)))
        {
            common.returnMessage(params, 400, 'Not enough args');
            return false;
        }
        let returnProfile = [];
        //Try to get the profile from cache. If it does not exist get from db and save it back to cache.
        /*cacheApi.getKey("pid"+userInfo.userid,function (err,result) {
            if(err || result ==null){
                common.db.collection('app_profiles' + params.qstring.app_id).findOne({'pid':userInfo.userid},function(err,userProfile){
                    if(userProfile){
                        cacheApi.setKey("pid"+userInfo.userid,JSON.stringify(userProfile));
                    }
                    processProfile(params,userProfile);

                });
            }
            else{
                let userProfile = JSON.parse(result);
                processProfile(params,userProfile);
            }
        });*/
        common.db.collection('app_users' + params.qstring.app_id).findOne({'did':userInfo.userid},function(err,deviceDetail){
            if(deviceDetail){
                let deviceProfile={};
                //deviceProfile.lastupdated = userProfile.lastupdated;
                deviceProfile.did = deviceDetail.did;
                deviceProfile.gender = (deviceDetail.gender)?deviceDetail.gender:"";
                deviceProfile.Interests = (deviceDetail.Interests)?deviceDetail.Interests:[];
                deviceProfile.wifihulls = (deviceDetail.wifihulls)?deviceDetail.wifihulls:[];
                deviceProfile.locationhull = (deviceDetail.locationhull)?deviceDetail.locationhull:[];
                deviceProfile.geo = {
                    cty:(deviceDetail.cty)?deviceDetail.cty:"",
                    cc:(deviceDetail.cc)?deviceDetail.cc:""
                };
                deviceProfile.sc = (deviceDetail.sc)?deviceDetail.sc:0;
                deviceProfile.tsd = (deviceDetail.tsd)?deviceDetail.tsd:0;
                deviceProfile.fs = (deviceDetail.fs)?deviceDetail.fs:0;
                deviceProfile.ls = (deviceDetail.ls)?deviceDetail.ls:0;
                deviceProfile.pushInterval = 1;
                deviceProfile.refname = (deviceDetail.history && deviceDetail.history.length>0)? deviceDetail.history[deviceDetail.history.length-1].refname : "";
                returnProfile.push(deviceProfile);
                common.returnOutput(params, returnProfile);
                //returnUserProfile(params,index,total,returnProfile);
                //index++;
            }
            else{
                common.returnOutput(params, returnProfile);
                //returnUserProfile(params,index,total,returnProfile);
                //index++;
            }
        });
    };

    let processProfile = function (params,userProfile) {
        let returnProfile = [];
        if(userProfile && userProfile.devices && userProfile.devices.length>0){
            let total = 1;//userProfile.devices.length;
            let index = 1;
            //userProfile.devices.forEach(function (device) {
                let deviceProfile={};
                let device = userProfile.devices[0];
                let app_user_id = common.crypto.createHash(semusiConfig.shaV1Hash).update(params.qstring.app_key + device + "").digest('hex');
                common.db.collection('app_users' + params.qstring.app_id).findOne({'_id':app_user_id},function(err,deviceDetail){
                    if(deviceDetail){
                        deviceProfile.lastupdated = userProfile.lastupdated;
                        deviceProfile.did = deviceDetail.did;
                        deviceProfile.gender = (deviceDetail.gender)?deviceDetail.gender:"";
                        deviceProfile.Interests = (deviceDetail.Interests)?deviceDetail.Interests:[];
                        deviceProfile.wifihulls = (deviceDetail.wifihulls)?deviceDetail.wifihulls:[];
                        deviceProfile.locationhull = (deviceDetail.locationhull)?deviceDetail.locationhull:[];
                        deviceProfile.geo = {
                            cty:(deviceDetail.cty)?deviceDetail.cty:"",
                            cc:(deviceDetail.cc)?deviceDetail.cc:""
                        };
                        deviceProfile.sc = (deviceDetail.sc)?deviceDetail.sc:0;
                        deviceProfile.tsd = (deviceDetail.tsd)?deviceDetail.tsd:0;
                        deviceProfile.fs = (deviceDetail.fs)?deviceDetail.fs:0;
                        deviceProfile.ls = (deviceDetail.ls)?deviceDetail.ls:0;
                        deviceProfile.pushInterval = 1;
                        deviceProfile.refname = (deviceDetail.history && deviceDetail.history.length>0)? deviceDetail.history[deviceDetail.history.length-1].refname : "";
                        returnProfile.push(deviceProfile);
                        common.returnOutput(params, returnProfile);
                        //returnUserProfile(params,index,total,returnProfile);
                        //index++;
                    }
                    else{
                        common.returnOutput(params, returnProfile);
                        //returnUserProfile(params,index,total,returnProfile);
                        //index++;
                    }

                });
            //});
        }
        else{
            common.returnOutput(params, returnProfile);
            return false;
        }
    }

    let returnUserProfile =function(params, index,total,result){
        if(index == total){
            common.returnOutput(params,result);
        }
    }

    /**
     * get users app information
     * @param send in request
     * @return array with json data
     **/
    usersApi.getAppUsers = function(params){
		common.db.collection('apps').find({'_id':common.db.ObjectID(params.qstring.app_id)}).toArray( function(err, app){
			if(err){
				logger.info(`Error While Retreiving App data`)
			}else{
				let maskedDids = false 
				if(app[0].features && app[0].features.maskedDids != undefined && app[0].features.maskedDids == true){
					 maskedDids = true 
				}
        if( params.qstring.app_id ){
			let match = {};
            let projection = {
              did: 1,
              _id: 1,
              sc: 1,
              fs: 1,
              tsd: 1,
              ls: 1,
              _custom: 1,
              _custom_UserId: 1,
              _custom_UserID: 1,
              p: 1,
            };
            if(params.qstring.type != ''){
                if(params.qstring.type == 'I'){
                    match = {"active":true};
                }else if(params.qstring.type == 'U'){
                    match = {"active":false};
                }

            }
            // search specific records
            if(params.qstring["search[value]"] != '' && params.qstring["search[value]"] != undefined){
				let matchs = {$or : [{"did":params.qstring["search[value]"]},{"_custom_UserId":params.qstring["search[value]"]},{"_custom_UserID":params.qstring["search[value]"]},{"_custom":params.qstring["search[value]"]},
				{"_custom":{ UserId: `["${params.qstring["search[value]"]}"]`}},
				{"_custom":{ UserId: `${params.qstring["search[value]"]}`}}, {"_custom_UserID":`["${params.qstring["search[value]"]}"]`}]}
               if(match.active != undefined){
                 matchs.active = match.active;
               }
               match = matchs;

               common.db.collection('app_users'+params.qstring.app_id).find(match,projection).toArray(function(err,data){
                    if(err){
						logger.error(`Error While Loading Single Device Id${err}`)
                        common.returnOutput(params,[]);
                    }
                    else{
                        if(data && data != undefined){
							
							common.db.collection('app_users'+params.qstring.app_id).count(function(error,resultCount){
								if(!error){ totalCount = resultCount; }
							});
							common.db.collection('timely_sessions').find({"_id":common.db.ObjectID(params.qstring.app_id)},{"android_active_users":1,"ios_active_users":1,"web_active_users":1}).toArray(function(err, data_timely) {
                                if(!err && data_timely)  {
										if(data_timely != null){ 
											let result = data_timely[0];
											if(params.qstring.type != ''){
												if(result.web == undefined){
													result.web = {};
													result.web['unknown'] = 0;
												}
												if(params.qstring.type == 'I'){
													totalCount  = (result.android_active_users?parseInt(result.android_active_users):0)+ (result.ios_active_users?parseInt(result.ios_active_users):0) + (result.web_active_users?parseInt(result.web_active_users):0);
												}else if(params.qstring.type == 'U'){
													totalCount  = totalCount - (result.android_active_users?parseInt(result.android_active_users):0)+ (result.ios_active_users?parseInt(result.ios_active_users):0) + (result.web_active_users?parseInt(result.web_active_users):0);
												}
											}
										}
										let usersList = [];
										data.forEach(function(value, key){
											if(value.p !==undefined){
											 if(params.qstring.platform == value.p){
												let ad = 0;
												// calculate average duration
												if(value.sc != undefined && value.tsd != undefined){
													let currentTotal = parseFloat((value.tsd/value.sc)).toFixed(2);
													if(currentTotal>=0){
														let manmins = parseInt(currentTotal/60000);
														let midmins = parseInt(manmins/10);
														let zmins;
														if(40 < midmins<50){
															zmins = parseInt(midmins/2);
														}else if(50 < midmins<100){
															zmins = parseInt(midmins/4);
														}else{
															zmins = 19;
														}
														if(zmins > 20){
															let mins=parseInt(zmins/4);
														}else{
															let mins=zmins;
														}
	
														let manseconds = parseInt(currentTotal-(mins*60000));
														let midseconds;
														if(0 < manseconds < 1000){
															midseconds = parseInt(manseconds/1000);
														}else if (1000 < manseconds < 100000){
															midseconds = parseInt(manseconds/10000);
														}else{
															midseconds = 39;
														}
														if(midseconds > 40){
															let seconds=parseInt(midseconds/1000);
														}else{
															let seconds=midseconds;
														}
														ad = mins+'<sub>Mins,</sub> '+seconds+'<sub>secs</sub>'
													}
												}
	

												const userIdObject = common.getUserIdObject(value);
												const ci = userIdObject ? userIdObject[0] : '';
												
									
												 logger.info(`ci :: ${ci}`);
												let clientId = maskedDids  ?  common.getMaskClientId(ci) : ci
												let obj = {
													alias       : (value.alias == undefined)? '<a href="dashboard?action=users#/users/details/'+value._id+'">'+value.did+'</a>': '<a href="dashboard?action=aud#/aud/user/details/'+value.did+'">'+value.alias+'</a>',
													did         : value.did,
													fs          : moment(value.fs*1000).format('MMM DD, YYYY'),
													ls          : (value.ls)? moment(value.ls*1000).format('MMM DD, YYYY'):'',
													sc          : (value.sc == undefined)? 0: value.sc,
													ad          : ad,
													ci          : clientId,
												}
	                                            
												usersList.push(obj);
											}
										}
	
											if(params.qstring.platform == undefined || params.qstring.platform == 'all'){
												let ad = 0;
												// calculate average duration
												if(value.sc != undefined && value.tsd != undefined){
													let currentTotal = parseFloat((value.tsd/value.sc)).toFixed(2);
													if(currentTotal>=0){
														let manmins = parseInt(currentTotal/60000);
														let midmins = parseInt(manmins/10);
														let zmins;
														if(40 < midmins<50){
															zmins = parseInt(midmins/2);
														}else if(50 < midmins<100){
															zmins = parseInt(midmins/4);
														}else{
															zmins = 19;
														}
														if(zmins > 20){
															let mins=parseInt(zmins/4);
														}else{
															let mins=zmins;
														}
	
														let manseconds = parseInt(currentTotal-(mins*60000));
														let midseconds;
														if(0 < manseconds < 1000){
															midseconds = parseInt(manseconds/1000);
														}else if (1000 < manseconds < 100000){
															midseconds = parseInt(manseconds/10000);
														}else{
															midseconds = 39;
														}
														if(midseconds > 40){
															let seconds=parseInt(midseconds/1000);
														}else{
															let seconds=midseconds;
														}
														ad = mins+'<sub>Mins,</sub> '+seconds+'<sub>secs</sub>'
													}
												}
	

												const userIdObject = common.getUserIdObject(value);
												const ci = userIdObject ? userIdObject[0] : '';

												 let clientId = maskedDids  ?  common.getMaskClientId(ci) : ci
												let obj = {
													alias       : (value.alias == undefined)? '<a href="dashboard?action=users#/users/details/'+value._id+'">'+value.did+'</a>': '<a href="dashboard?action=aud#/aud/user/details/'+value.did+'">'+value.alias+'</a>',
													did         : value.did,
													fs          : moment(value.fs*1000).format('MMM DD, YYYY'),
													ls          : (value.ls)? moment(value.ls*1000).format('MMM DD, YYYY'):'',
													sc          : (value.sc == undefined)? 0: value.sc,
													ad          : ad,
													ci          : clientId,
												}

												usersList.push(obj);
											}
											
										});
										if(params.qstring["search[value]"] != '' && params.qstring["search[value]"] != undefined){
											totalCount = usersList.length;
										}
										let obj = {
											"draw": parseInt(params.qstring.draw),
											"recordsTotal": totalCount,
											"recordsFiltered": totalCount,
											"data": usersList
										}
										common.returnOutput(params, obj);
					            } else {
									common.returnOutput(params,{});
					            }
					        });
                        }
                        else{
                            common.returnOutput(params,{});
                        }
                    }
                })
            }
            else{
				let totalCount;
                let sortlist = ['did','fs','ls','sc','tsd'];
                let sort = {fs:1}
                // check sort by
                if(params.qstring["order[0][column]"]
                    || (params.qstring["order[0][column]"] && params.qstring["order[0][column]"] == 0)){
                    // check filter column
                    if(params.qstring["order[0][column]"]){
                        sort = {}
                        sort[sortlist[params.qstring["order[0][column]"]]] = (params.qstring["order[0][dir]"] == 'asc')? 1 : -1;
                    }
				}
				
				common.db.collection('app_users'+params.qstring.app_id).count(function(error,resultCount){
					if(!error){ totalCount = resultCount; }
				});
				
				if(params.qstring.platform!="all"){
					match.p = params.qstring.platform
				}
                // get pagination start
				params.qstring.start = parseInt(params.qstring.start); //(parseInt(params.qstring.draw) -1)*parseInt(params.qstring.length);
                common.db.collection('app_users'+params.qstring.app_id).find(match,projection).sort(sort).skip(params.qstring.start).limit(parseInt(params.qstring.length)).toArray(function(err,data){
                    if(err){
						logger.error(`getAppUsers=> ${err}`); 
                        common.returnOutput(params,[]);
                    }
                    else{
                        if(data && data != undefined){
							common.db.collection('app_users'+params.qstring.app_id).count(function(error,resultCount){
								if(!error){ totalCount = resultCount; }
							});
							common.db.collection('timely_sessions').find({"_id":common.db.ObjectID(params.qstring.app_id)},{"android_active_users":1,"ios_active_users":1,"web_active_users":1}).toArray(function(err, data_timely) {
								
                                if(!err && data_timely)  {
										if(data_timely != null){ 
											let result = data_timely[0];
											if(params.qstring.type != ''){
												if(result.web == undefined){
													result.web = {};
													result.web['unknown'] = 0;
												}
												if(params.qstring.type == 'I'){
													totalCount  = (result.android_active_users?parseInt(result.android_active_users):0)+ (result.ios_active_users?parseInt(result.ios_active_users):0) + (result.web_active_users?parseInt(result.web_active_users):0);
												}else if(params.qstring.type == 'U'){
													totalCount  = totalCount - (result.android_active_users?parseInt(result.android_active_users):0)+ (result.ios_active_users?parseInt(result.ios_active_users):0) + (result.web_active_users?parseInt(result.web_active_users):0);
												}
											}
										}
										let usersList = [];
										data.forEach(function(value, key){
											if(value.p!==undefined){
											 if(params.qstring.platform == value.p){
												let ad = 0;
												// calculate average duration
												if(value.sc != undefined && value.tsd != undefined){
													let currentTotal = parseFloat((value.tsd/value.sc)).toFixed(2);
													if(currentTotal>=0){
														let manmins = parseInt(currentTotal/60000);
														let midmins = parseInt(manmins/10);
														let zmins;
														if(40 < midmins<50){
															zmins = parseInt(midmins/2);
														}else if(50 < midmins<100){
															zmins = parseInt(midmins/4);
														}else{
															zmins = 19;
														}
														if(zmins > 20){
															let mins=parseInt(zmins/4);
														}else{
															let mins=zmins;
														}
	
														let manseconds = parseInt(currentTotal-(mins*60000));
														let midseconds;
														if(0 < manseconds < 1000){
															midseconds = parseInt(manseconds/1000);
														}else if (1000 < manseconds < 100000){
															midseconds = parseInt(manseconds/10000);
														}else{
															midseconds = 39;
														}
														if(midseconds > 40){
															let seconds=parseInt(midseconds/1000);
														}else{
															let seconds=midseconds;
														}
														ad = mins+'<sub>Mins,</sub> '+seconds+'<sub>secs</sub>'
													}
												}
	

												const userIdObject = common.getUserIdObject(value);
												const ci = userIdObject ? userIdObject[0] : '';
												
												 let clientId = maskedDids  ?  common.getMaskClientId(ci) : ci
	
												let obj = {
													alias       : (value.alias == undefined)? '<a href="dashboard?action=users#/users/details/'+value._id+'">'+value.did+'</a>': '<a href="dashboard?action=aud#/aud/user/details/'+value.did+'">'+value.alias+'</a>',
													did         : value.did,
													fs          : moment(value.fs*1000).format('MMM DD, YYYY'),
													ls          : (value.ls)? moment(value.ls*1000).format('MMM DD, YYYY'):'',
													sc          : (value.sc == undefined)? 0: value.sc,
													ad          : ad,
													ci          : clientId,
												}
												
												usersList.push(obj);
											}
										}
	
											if(params.qstring.platform == undefined || params.qstring.platform == 'all'){
												let ad = 0;
												// calculate average duration
												if(value.sc != undefined && value.tsd != undefined){
													let currentTotal = parseFloat((value.tsd/value.sc)).toFixed(2);
													if(currentTotal>=0){
														let manmins = parseInt(currentTotal/60000);
														let midmins = parseInt(manmins/10);
														let zmins;
														if(40 < midmins<50){
															zmins = parseInt(midmins/2);
														}else if(50 < midmins<100){
															zmins = parseInt(midmins/4);
														}else{
															zmins = 19;
														}
														// Define seconds with default value to make accessible 
														let mins = zmins;
														if(zmins > 20){
															mins=parseInt(zmins/4);
														}
	
														let manseconds = parseInt(currentTotal-(mins*60000));
														let midseconds;
														if(0 < manseconds < 1000){
															midseconds = parseInt(manseconds/1000);
														}else if (1000 < manseconds < 100000){
															midseconds = parseInt(manseconds/10000);
														}else{
															midseconds = 39;
														}
														// Define seconds with default value to make accessible 
														let seconds=midseconds;
														if(midseconds > 40){
															 seconds=parseInt(midseconds/1000);
														}
														ad = mins+'<sub>Mins,</sub> '+seconds+'<sub>secs</sub>'
													}
												}
	
												let viewed = (value.campaigns && value.campaigns.viewed)?value.campaigns.viewed.length:0;
												let clicked = (value.campaigns && value.campaigns.clicked)?value.campaigns.clicked.length:0;
												const userIdObject = common.getUserIdObject(value);
												const ci = userIdObject ? userIdObject[0] : '';


												 let clientId = maskedDids  ?  common.getMaskClientId(ci) : ci
												let obj = {
													alias       : (value.alias == undefined)? '<a href="dashboard?action=users#/users/details/'+value._id+'">'+value.did+'</a>': '<a href="dashboard?action=aud#/aud/user/details/'+value.did+'">'+value.alias+'</a>',
													did         : value.did,
													fs          : moment(value.fs*1000).format('MMM DD, YYYY'),
													ls          : (value.ls)? moment(value.ls*1000).format('MMM DD, YYYY'):'',
													sc          : (value.sc == undefined)? 0: value.sc,
													ad          : ad,
													ci          : clientId,
												}

												usersList.push(obj);
											}
											
										});
										if(params.qstring["search[value]"] != '' && params.qstring["search[value]"] != undefined){
											totalCount = usersList.length;
										}
										let obj = {
											"draw": parseInt(params.qstring.draw),
											"recordsTotal": totalCount,
											"recordsFiltered": totalCount,
											"data": usersList
										}
										common.returnOutput(params, obj);
					            } else {
									common.returnOutput(params,{});
					            }
					        });
                        }
                        else{
                            common.returnOutput(params,{});
                        }
                    }
                })
            }
        }
        else{
            common.returnOutput(params,{});
        }
	   }
	 })
    }

    /**
     * get users app information
     * @param send in request
     * @return array with json data
     **/
      usersApi.getUserDetails = function(params){

		common.db.collection('apps').find({'_id':common.db.ObjectID(params.qstring.app_id)}).toArray( function(err, app){
			if(err){
				logger.info(`Error While Retreiving App data`)
			}else{
				let maskedDids = false 
				if(app[0].features && app[0].features.maskedDids != undefined && app[0].features.maskedDids == true){
					 maskedDids = true 
				}


        if( params.qstring.did && params.qstring.app_id){
            common.db.collection('app_users'+params.qstring.app_id).findOne({_id:params.qstring.did},{"did":1, "alias":1, "userAttributes":1,"competingapps":1,"campaigns":1, "ls":1, "lsd":1, "le":1, "_custom_UserId":1,"_custom_UserID": 1, "_custom": 1, "d":1, "c":1, "av":1,"tz":1,"Interests":1,"_app_version": 1}, function(err,data){
                if(err){
                    common.returnOutput(params,[]);
                }
                else{
                    if(data && data != undefined){
						//format user_ID 
						let ci;
						if(data._custom) { 
							ci = common.formatClientId(data._custom);
						  }else{
							if(data._custom_UserId){ 
								if(common.isJson(data._custom_UserId)) {
									ci = JSON.parse(data._custom_UserId)[0];
								}else {
									ci = data._custom_UserId;
								}       
							}
							else if(data._custom_UserID) {
								if(common.isJson(data._custom_UserID)) {
									ci = JSON.parse(data._custom_UserID)[0];
								}else {
									ci = data._custom_UserID;
								} 
							}
							else{
								ci='';
							}
						 }

						 //Set Client ID 
						data['ci'] = ci;

                        common.db.collection('eventhistory_'+params.qstring.app_id).findOne({_id:data._id},{"el":1}, function(err,dataset){
                            if(err || dataset == null){
								logger.error(`Error While Loading Event History Or Data is Null ERR ${err} and Dataset  ${dataset}`)
                                common.returnOutput(params,[]);
                            }else{
								data.ci =( maskedDids ) ?  common.getMaskClientId(data.ci) : data.ci
								delete data._custom_UserId;
								delete data._custom;
                                let result = {"data":data,"eventlist":dataset};
                                common.returnOutput(params, result);
                            }
                        });
                    }
                    else{
                        common.returnOutput(params,[]);
                    }
                }
            });
        }else{
            common.returnOutput(params,[]);
        }
	}
	})
    }


    /**
     * get users app information
     * @param send in request
     * @return array with json data
     **/
    usersApi.getUserRecentActivity = function(params){

        if( params.qstring.did && params.qstring.app_id){

            let currentEpoch = moment();
            let lastSevenDay = (moment(currentEpoch).subtract(7,'days').valueOf())/1000;
            /*let arr = [
                    {$match:{"eventTime":{$gte:lastSevenDay}, "did":params.qstring.did}},
                    {$project:{"key":1, "eventTime":1, "segment":1, "_id":0}},
                    { $sort: {"eventTime":-1} },
                    {$limit:5}
                ];*/
             
            common.db.collection('events_'+params.qstring.app_id).aggregate(
                [
                    {$match:{"eventTime":{$gte:lastSevenDay}, "did":params.qstring.did}},
                    {$project:{"key":1, "eventTime":1, "segment":1, "_id":0}},
                    { $sort: {"eventTime":-1} },
                    {$limit:5}
                ], function(err,data){
                    if(err){
						logger.error(`in error=> ${err}`);
                        common.returnOutput(params,[]);
                    }
                    else{
                        logger.info(`data ::: ${data}`);  
                        if(data && data != undefined){
                            common.returnOutput(params, data);
                        }
                        else{
                            common.returnOutput(params,[]);
                        }
                    }
            });
        }else{
            common.returnOutput(params,[]);
        }
    }

    /**
    * get app and users information
    * @param send in request
    * @return array with json data
    **/
    let appAndUserInf={};
    usersApi.getAppAndUserData = function(params){
    		appAndUserInf={};

    		if(!params.qstring.device_id || params.qstring.device_id == undefined){
    			common.returnMessage(params, 400, 'Device id is missing');
    			return false;
    		}

    		params.qstring.did = params.qstring.device_id;

    		async.parallel([
    			function(callback){
    				if(params.qstring.activeModules && params.qstring.activeModules != 'false'){
    					appAndUserInf.activeModules = [];

    					if(params.qstring.sdk_version && params.qstring.sdk_i_version && params.qstring.sdk_i_version > 212 ){
    						cacheApi.getKey('appModules_data_'+ params.qstring.app_id,function(err,result){
    							if(err || result==null){
    								common.db.collection('apps').findOne({_id:common.db.ObjectID(params.qstring.app_id)},function(err,result){
    									let data=[];
    									if(!err && result){
    										common.db.collection('appmodules').find({_id:{$in:result.modules}}).toArray(function(err,modules){
    											if(modules){
    												modules.forEach(function(item){
    													if(item.arch==params.qstring.arch && item.platform==params.qstring.platform){
    														data.push(item);
    													}
    												});
    											}
    											cacheApi.setKey('appModules_data_'+ params.qstring.app_id,JSON.stringify(data));
    											appAndUserInf.activeModules = data;
    											callback();
    										});
    									}
    									else{
    										callback();
    									}
    								});
    							}
    							else{
    								let data = [];
    								data = JSON.parse(result);
    								appAndUserInf.activeModules = data;
    								callback();
    							}
    						});
    					}
    					else{
    						callback();
    					}
    				}
    				else{
    					callback();
    				}
    			},
    			function(callback){
    				appAndUserInf.userProfile = [];
    				if(params.qstring.userProfile && params.qstring.userProfile != 'false'){
    					appAndUserInf.userProfile = [];
    					cacheApi.getKey("attrubution_"+params.qstring.app_id,function (err,result) {
    						if(err || result ==null){
    							common.db.collection('apps').findOne({'_id':common.db.ObjectID(params.qstring.app_id), "attribution":{$exists:true}},{"attribution":1,"_id":0},function(err,app){
    								if(app){
    									appAndUserInf.userProfile.push(app);
    									cacheApi.setKey("attrubution_"+params.qstring.app_id,JSON.stringify(app));
    								}
    							});
    						}
    						else{
    							appAndUserInf.userProfile.push(JSON.parse(result));
    						}
    					});

    					common.db.collection('app_users' + params.qstring.app_id).findOne({'did':params.qstring.did},function(err,deviceDetail){
    						if(deviceDetail){
    							let deviceProfile={};
    							//deviceProfile.lastupdated = userProfile.lastupdated;
    							deviceProfile.did = deviceDetail.did;
    							deviceProfile.gender = (deviceDetail.gender)?deviceDetail.gender:"";
    							deviceProfile.Interests = (deviceDetail.Interests)?deviceDetail.Interests:[];
    							deviceProfile.wifihulls = (deviceDetail.wifihulls)?deviceDetail.wifihulls:[];
    							deviceProfile.locationhull = (deviceDetail.locationhull)?deviceDetail.locationhull:[];
    							deviceProfile.geo = {
    								cty:(deviceDetail.cty)?deviceDetail.cty:"",
    								cc:(deviceDetail.cc)?deviceDetail.cc:""
    							};
    							deviceProfile.sc = (deviceDetail.sc)?deviceDetail.sc:0;
    							deviceProfile.tsd = (deviceDetail.tsd)?deviceDetail.tsd:0;
    							deviceProfile.fs = (deviceDetail.fs)?deviceDetail.fs:0;
    							deviceProfile.ls = (deviceDetail.ls)?deviceDetail.ls:0;
    							deviceProfile.pushInterval = 1;
    							if(deviceDetail.history && deviceDetail.history[deviceDetail.history.length-1]){
    								if(deviceDetail.history[deviceDetail.history.length-1].refname){
    									deviceProfile.refname = deviceDetail.history[deviceDetail.history.length-1].refname;
    								}
    							}
    							else{
    								deviceProfile.refname = '';
    							}

    							appAndUserInf.userProfile.push(deviceProfile);
    						}
    						callback();
    					});
    				}
    				else{
    					callback();
    				}
    			},
    			function(callback){
    				if(params.qstring.competingApps && params.qstring.competingApps != 'false' ){
    					appAndUserInf.competingApps = [];

    					cacheApi.getKey("otherapp_"+params.qstring.app_id,function(err,cacheResult){
    						if(err || cacheResult==null){
    							common.db.collection('otherapp_groups').find({appid:params.qstring.app_id, deleted : false}).toArray(function(err,data){
    								if(err){
										logger.error(`error=> ${err}`);	 
    								}
    								else{
    									if(data && data.length > 0){
    										let appList = [];
    										data.forEach(function(alist){
    											alist.list.forEach(function(app){
    												appList.push(app)
    											});
    										});

    										cacheApi.setKey("otherapp_"+params.qstring.app_id,JSON.stringify(appList));
    										appAndUserInf.competingApps = appList;
    									}
    								}

    								callback();
    							});
    						}
    						else{
    							appAndUserInf.competingApps = JSON.parse(cacheResult);
    							// end events loops
    							callback();
    						}
    					});
    				}
    				else{
    					callback();
    				}
    			},
    			function(fnCallback){

    				// check campaign request
    				if(params.qstring.activeCampaigns && params.qstring.activeCampaigns != 'false'  ){
    					appAndUserInf.activeCampaigns = [];
    					// check campaign into cache
    					cacheApi.getKey('campaigns_'+ params.qstring.app_id,function(err,result){
    						if(err || result==null){
								logger.info("getActiveCampaigns call db");
    							let commands=[];
    							let retVal=[];
    							let retValAll=[];
    							let feedBackCampaignExists = false;
    							let audId="";
    							let cid=null;
    							let atRiskCommand = false;

    							//Build match criteria.
    							let match = {};
    							let unwind = {};
    							if(params.qstring.lastPullTime){
    								match.ud = {$gt:parseInt(params.qstring.lastPullTime)};
    							}

    							if(params.qstring.command){
    								commands = JSON.parse(params.qstring.command);
    								//When there is request for a particular campaign don not send default feedback campaign.
    								if(commands.length>0){
    									cid = commands[0].cid;
    									match._id=common.db.ObjectID(cid);
    									feedBackCampaignExists = true;
    									if(commands[0].action=="atrisk"){
    										atRiskCommand = true;
    									}
    								}
    							}

    							if(params.qstring.childAppId && params.qstring.childAppId!==""){
    								//If ud is not set then it is the first call to get active campaigns.
    								if(!match.ud){
    									match.st = "ACTIVE";
    									match['childIds.approved'] = true;
    									match['childIds.deleted'] = false;
    								}
    								match['childIds.id'] = params.qstring.childAppId;
    								unwind = {$unwind:"$childIds"};
    							}

    							else{
    								if(!match.ud){
    									match.st = "ACTIVE";
    								}
    								//This is to avoid unwind error
    								unwind = {"$match":match};
    							}

    							match.t = {$nin:['SMS', 'Email']};
    							common.db.collection('campaigns_'+ params.qstring.app_id).aggregate([
    								unwind,
    								{$match:match},
    								{$sort:{ud: -1,_id:1}}

    							],function(err,campaigns){
    								 
    								if(err){
										logger.error(`error=> ${err}`);
    									callback(err, null)
    									return false;
    								}
    								 
    								async.forEach(campaigns, function(item, callback){
    									let addCampaign = true; // This flag is used to control atrisk campaigns.
    									let childApproved,childDeleted,sdkDeletedFlag;
    									if(item.childIds && item.childIds.length>0){
    										if(item.childIds.approved==true){
    											childApproved = true;
    										}
    										else{
    											childApproved = false;
    										}
    										if(item.childIds.deleted==true){
    											childDeleted = true;
    										}
    										else{
    											childDeleted = false;
    										}

    										sdkDeletedFlag = (item.st == "ACTIVE" && childApproved && !childDeleted)?false:true;
    									}
    									else{
    										sdkDeletedFlag = (item.st == "ACTIVE")?false:true;
    									}
    									let obj={};
    									obj._id=item._id;
    									obj.tid=item.tid;
    									obj.t=item.t;
    									obj.sd=item.sd;
    									obj.ed=item.ed;
    									obj.ud = item.ud;
    									obj.d = sdkDeletedFlag;
    									obj.delays = (item.delays)? item.delays : 0;
                      obj.delayi = (item.delayi)? item.delayi : 0;
                      obj.delayu = (item.delayu)? item.delayu : 'Minutes';
    									obj.fre = {};
    									if(item.c_event){
    										obj.c_event = item.c_event;
    										obj.c_period = item.c_period;
    									}
    									obj.fre.dp = 0;
    									if(item.d==false){
    										obj.fre=item.fre;
    										obj.fre.dp = 0;
    										if(item.daySelector=="ALLWEEK"){
    											obj.allweek=true;
    											obj.days=[];
    										}
    										else{
    											obj.allweek=false;
    											obj.days=item.days;
    										}
    										if(item.timeSelector=="ALLDAY"){
    											obj.allday=true;
    										}
    										else{
    											obj.allday=false;
    											if(item.time && item.time.length>0){
    												obj.time={start:item.time[0],end:item.time[1]};
    											}
    											else{
    												obj.time={start:8,end:20};
    											}

    										}
    									}
    									if(item.t!=="FEEDBACK"){
    										async.parallel(
    											[
    												function(callback){
    													if(obj.tid){
    														 
    														common.db.collection('templates_'+ params.qstring.app_id).findOne({"isTemplateDeleted":"false","_id": common.db.ObjectID(obj.tid)},function (err, tool) {
    															 
    															if(err){
    																callback(err);
    															}
    															else{
    																if(tool && item.d==false){
    																	obj.data={};
    																	let data={};
    																	switch(obj.t){
    																		case "PUSH":
                                        tool.template._id = tool._id;
    																		populatePushObject(tool.template,data, params);
    																		obj.data = data;
    																		break;
    																		case "IN-APP":
    																		populateInAppObject(tool.template,data);
    																		obj.data = data;
    																		break;
    																		case "RATING":
    																		populateRatingObject(tool.template,data);
    																		obj.data = data;
    																		break;
    																		case "SURVEY":
    																		let q = [];
    																		populateSurveyObject(tool.template,q);
    																		obj.data.q = q;
    																		break;
    																	}
    																}
    																callback();
    															}
    														});
    													}
    													else{
    														callback()
    													}
    												},
    												function(callback){
    													 
    													if(item.aud==undefined || item.aud=="" ){
    														audId = common.db.ObjectID();
    													}
    													else{
    														audId = common.db.ObjectID(item.aud);
    													}
    													common.db.collection('audiencesegment_'+ params.qstring.app_id).findOne({"_id": audId, "$or": [{'isDeleted':false},{'isDeleted':null}]},function(err,audience){
    														if(err){
    															callback(err);
    														}
    														else{
    															if(audience && item.d==false){
    																obj.aud = audience.segmentinfo;
    																if(obj.aud.who && obj.aud.who.length > 0){
    																	obj.aud.who.forEach(function(who){
      																		if(who.operand == 'did' && who.operator == 'in' && who.value.indexOf(';') >= 0){
      																			who.value = who.value.split(';');
      																		}
                                          // validate custom let and send array if select in operator
                                          if ( who.operand.indexOf('_custom') >= 0 && who.operator == 'in' && who.value.indexOf(',') >= 0) {
                                              who.value = who.value.split(',');
                                          }
                                          else if (who.operand.indexOf('_custom') >= 0 && who.operator == 'in'){
                                              who.value = [who.value];
                                          }
    																	})
    																}

    																//Do not send atrisk active campaigns when devices pulls active campaigns.
    																if(audience.name == semusiConfig.atRiskAudience && !atRiskCommand){
    																	addCampaign = false;
    																}
    																if(isStaticAudience(obj.aud)){
    																	if(obj.t=="PUSH"){
    																		obj.fre.dp=1;
    																	}
    																}
    															}
    															else{
    																//Add a key to indicate this campaign is sent via direct push, and handle its frequency by doing -1.
    																if(obj.t=="PUSH"){
    																	obj.fre.dp=1;
    																}

    															}
    															callback();
    														}
    													});

    												}
    											],function(err){
    												if(err){
    													callback(err);
    												}
    												else{
    													// dump all valid campaigns that will store in cache below of code
    													if(addCampaign){
    														obj.st = parseInt(obj.ud)+1; //This is used on the client side to manage fetching of active campaigns and ignore device time if it is wrong. Add 1 milisecond if user query on database like $gt.
    														retValAll.push(obj);
    													}

    													// validate campaign now or delayed
														logger.info(`item.delays :: +> ${item.delays}`);
    													if(obj.delays && obj.aud && obj.aud.what && (obj.aud.what.length == 1 || obj.aud.what.length == 0) ){
    														validateDelayedCampaign(obj, params, function(error, campData, status){
																logger.info(`db retVal error, campData, status::: ${error}  :: ${campData} :: ${status}`)
    																if(!error && campData){
    																	// prepare live event property
    														          prepareLiveEvents(campData.e, obj, true);
    														          retVal.push(obj);
    																}
    																else if(status){
    																	retVal.push(obj);
    																}
    																callback();
    														});
    													}
    													else{
    														// validate live event and check user is able or not for this campaign
    														if(obj.aud && obj.aud.what && obj.aud.what.length > 0){
																logger.info("check campaign segment ");
    															campaignCommon.validateCampaignUser(params.qstring.app_id, item._id, params.qstring.device_id, function(error, isValidate, uData){
																	logger.error(`error :: ${error}`);
																	logger.info(`isValidate :: ${isValidate}`);
    																// allow campaign if user is valid for this campaign
    																if(isValidate){
    																	if(addCampaign){
    																		if(uData.e){
    																			// prepare live event property
    																			prepareLiveEvents(uData.e, obj, true);
    																		}
    																		retVal.push(obj);
    																	}
    																}
    																else if(isValidate === false && uData == null){
    																	// prepare live event property
    																	prepareLiveEvents(obj.aud.what, obj, false);
    																	retVal.push(obj);
    																}
    																// allow campaign if not exists in cache
    																else if(error){
    																	if(addCampaign){
    																		retVal.push(obj);
    																	}
    																}
    																callback();
    															});
    														}
    														else{ // non live event campaign
																logger.info("check not campaign segment ");
    															if(addCampaign){
    																retVal.push(obj);
    															}
    															callback();
    														}
    													}
    												}
    											});
    										}
    										else{
    											feedBackCampaignExists = true;
    											retValAll.push(obj);
    											retVal.push(obj);
    											callback();
    										}


    									},function(error){
    										if (error){
												logger.error(`EEOR: Return campaign data. ${error}`);
    											//Do something on error
    											appAndUserInf.activeCampaigns = retVal;
    										}else{
    											//Return final data.
    											if(!feedBackCampaignExists){
    												if(params.defaultuninstallcampaign == true || params.defaultuninstallcampaign==undefined && !atRiskCommand){
    													retVal.push(returnDefaultFeedbackCampaign(params.appCreatedOn));
    													retValAll.push(returnDefaultFeedbackCampaign(params.appCreatedOn));
    												}
    											}
    											cacheApi.setKey('campaigns_'+ params.qstring.app_id,JSON.stringify(retValAll));
												logger.info(`${params.qstring.device_id} == get from db == ${retVal}`)
    											appAndUserInf.activeCampaigns = retVal;
    											fnCallback();
    										}
    									});
    								});
    							} else{
									logger.info("got in cache");
    								let data = '';
    								let lastPullTimeCamp = [];
    								data = JSON.parse(result);
    								if(params.qstring.lastPullTime){
    									// data.forEach(function(item){
    									//     if(parseInt(item.ud) > parseInt(params.qstring.lastPullTime)){
    									//         lastPullTimeCamp.push(item)
    									//     }
    									// });
    									logger.info("with last pull time");
    									async.forEach(data, function(item, callback){
											logger.info(`cache lastPullTime item.delays :: ${item.delays} :: id:: ${item._id}`);
    										if(item.delays && item.aud && item.aud.what && ( item.aud.what.length == 1 || item.aud.what.length == 0) ){
    											validateDelayedCampaign(item, params, function(error, campData, status){
													logger.error(`cache lastPullTimeCamp error:>>>> ${error}`);
													logger.info(`cache lastPullTimeCamp campdata , status =>>> ${campData}::${status} `)
    												if(!error && campData){
														logger.info(`cache lastPullTimeCamp campData::=>> ${campData}`);
    													// prepare live event property
    											        prepareLiveEvents(campData.e, item, true);
    											        lastPullTimeCamp.push(item);
    												}
    												else if(status){
    												lastPullTimeCamp.push(item);
    											}
    												callback();
    											});
    										}
    										else{
    											if(parseInt(item.ud) > parseInt(params.qstring.lastPullTime)){
    												logger.info("varify ud check "); 
    												if(item.aud && item.aud.who && item.aud.who.length > 0){
    													item.aud.who.forEach(function(who){
      														if(who.operand == 'did' && who.operator == 'in' && who.value.indexOf(';') >= 0){
      															who.value = who.value.split(';');
      														}
                                  // validate custom let and send array if select in operator
                                  if ( who.operand.indexOf('_custom') >= 0 && who.operator == 'in' && who.value.indexOf(',') >= 0) {
                                      who.value = who.value.split(',');
                                  }
                                  else if (who.operand.indexOf('_custom') >= 0 && who.operator == 'in'){
                                      who.value = [who.value];
                                  }
    													})
    												}

    												// validate live event and check user is able or not for this campaign
    												if(item.aud && item.aud.what && item.aud.what.length > 0){
    													logger.info("check in cache "); 
    													campaignCommon.validateCampaignUser(params.qstring.app_id, item._id, params.qstring.device_id, function(error, isValidate, uData){
															logger.info(`isValidate =>>${isValidate}`);
															logger.error(`error=> ${error}`);
    														// allow campaign if user is valid for this campaign
    														if(isValidate){
    															if(uData.e){
    																// prepare live event property
    																prepareLiveEvents(uData.e, item, true);
    															}
    															lastPullTimeCamp.push(item);
    														}
    														else if(isValidate === false && uData == null){
    															// prepare live event property
    															prepareLiveEvents(item.aud.what, item, false);
    															lastPullTimeCamp.push(item);
    														}
    														// allow campaign if not exists in cache
    														else if(error){
    															lastPullTimeCamp.push(item);
    														}
    														callback();
    													});
    												}
    												else{ // non live event campaign
														logger.info("with out segment info");
    													lastPullTimeCamp.push(item);
    													callback();
    												}
    											}
    											else{
    												callback();
    											}
    										}

    									}, function(err){
    										if(lastPullTimeCamp.length == 0 ){
    											if(!feedBackCampaignExists){
    												if(params.defaultuninstallcampaign == true || params.defaultuninstallcampaign==undefined && !atRiskCommand){
    													lastPullTimeCamp.push(returnDefaultFeedbackCampaign(params.appCreatedOn));
    													appAndUserInf.activeCampaigns = lastPullTimeCamp;
    													fnCallback();
    												}
    											}
    										}else{
    											appAndUserInf.activeCampaigns = lastPullTimeCamp;
												logger.info(`${params.qstring.device_id} === cache lastPullTimeCamp ${lastPullTimeCamp}`)
    											fnCallback();
    										}
    									});
    								}else{
    									 
    									let camps = [];
    									//data.forEach(function(item){
    									async.forEach(data, function(item, callback){
    										// check delayed unit into campaign
											logger.info(`cache item.delays:: ${item.delays} :: _id=> ${item._id}`)

    										if(item.delays && item.aud && item.aud.what && ( item.aud.what.length == 1 || item.aud.what.length == 0 ) ){
    											validateDelayedCampaign(item, params, function(error, campData, status){
													logger.error(`cache error ==>>> ${error}`)
													logger.info(`campDAta:: status==>> ${campData} :: ${status}`)
    												if(!error && campData){
														logger.info(`cache camps campData =>> ${campData}`)
    													// prepare live event property
    													prepareLiveEvents(campData.e, item, true);
    													camps.push(item);
    												}
    												else if(status){
    													camps.push(item);
    												}

    												callback();
    											});
    										}
    										else{
    											logger.info("varify ed check ");
    											if(parseInt(item.ed) > (common.getCurrentEpochTime()*1000)){
    												logger.info("check segmentinfo ");
    												if(item.aud && item.aud.who && item.aud.who.length > 0){
    													item.aud.who.forEach(function(who){
      														if(who.operand == 'did' && who.operator == 'in' && who.value.indexOf(';') >= 0){
      															who.value = who.value.split(';');
      														}

                                  // validate custom let and send array if select in operator
                                  if ( who.operand.indexOf('_custom') >= 0 && who.operator == 'in' && who.value.indexOf(',') >= 0) {
                                      who.value = who.value.split(',');
                                  }
                                  else if (who.operand.indexOf('_custom') >= 0 && who.operator == 'in'){
                                      who.value = [who.value];
                                  }
    													});
    													logger.info(`item.aud.who ==>" ${item.aud.who}`); 
    												}

    												// validate live event and check user is able or not for this campaign
    												if(item.aud && item.aud.what && item.aud.what.length > 0){
														logger.info("check campaign in cache ");
    													campaignCommon.validateCampaignUser(params.qstring.app_id, item._id, params.qstring.device_id, function(error, isValidate, uData){
															logger.info(`isValidate  && uData =>>${isValidate} && ${uData}`);
															logger.error(`error=> ${error}`);
    														// allow campaign if user is valid for this campaign
    														if(isValidate){
    															if(uData.e){
    																// prepare live event property
    																prepareLiveEvents(uData.e, item, true);
    															}
    															camps.push(item);
    														}
    														else if(isValidate === false && uData == null){
    															// prepare live event property
    															prepareLiveEvents(item.aud.what, item, false);
    															camps.push(item);
    														}
    														// allow campaign if not exists in cache
    														else if(error){
    															camps.push(item);
    														}
    														callback();
    													});
    												}
    												else{ // non live event campaign
														logger.info("with out segmentinfo ");
    													camps.push(item);
    													callback();
    												}
    											}
    											else{
    												callback();
    											}
    										}
    									}, function(err){
    										appAndUserInf.activeCampaigns = camps;
											logger.info(`${params.qstring.device_id}==== cache camps  ${camps}`)
    										fnCallback();
    									});
    								}
    							}
    						});
    					}
    					else{
    						fnCallback();
    					}
    				},
            function(fnCallback){
        				// check transactional campaign request
        				if(params.qstring.activeTransactional){
        					cacheApi.getKey('trans_campaigns_'+ params.qstring.app_id,function(err,result){
        						if(err || result==null){
        							logger.info("getActiveCampaigns transactional call db"); 
        							let retVal=[];
        							let retAllVal=[];
        							common.db.collection('transaction_camp_'+ params.qstring.app_id).find({}).toArray(function(err,campaigns){
        								if(err){
											logger.error(`error=> ${err}`);
        									appAndUserInf.activeTransCampaigns = retVal;
        								}
        								else{
        									campaigns.forEach(function(camp){
                            if(camp.payload ){
            										camp.payload.camp_id = camp._id;
                                camp.payload.cmd = "#transactional";
            										// validate cdata if it's more than 2kb send url to get form db
            										if(camp.payload && camp.payload.cdata){
            											if(common.calculateDataSize(camp.payload.cdata)  > common.config.campaignPayloadSize ){
            												camp.payload.cdata = common.config.host+"/o/templates/getCustomData?app_id="+params.qstring.app_id+"&api_key="+params.qstring.api_key+"&tid="+camp._id+"&ct=trans";
            											}
            										}

                                // validate lastTPullTime key that is exists or not in request
                  							if(params.qstring.lastTPullTime){
                  							  // validate st key in campaign
                  							  if(camp.payload.st == undefined){
                  								  camp.payload.st = camp.createdAt
                  							  }

                  							  // compare lastTPullTime and campaign start time to get lasted campaign
                  							  if(params.qstring.lastTPullTime < camp.payload.st){
                  								  retVal.push(camp.payload);
                  							  }
                  							}
                  							else{
                  								retVal.push(camp.payload);
                  							}

            										retAllVal.push(camp.payload);
                            }
        									});

                          if(retAllVal.length > 0){
                              cacheApi.setKey('trans_campaigns_'+ params.qstring.app_id,JSON.stringify(retAllVal));
                          }

        									appAndUserInf.activeTransCampaigns = retVal;
        								}
        							});
        						}
        						else {
									logger.info("got in transaction from cache");
        							let data = JSON.parse(result);
        							if(params.qstring.lastTPullTime){
        								let retVal = [];
        								data.forEach(function(camp){
                          if(camp.payload ){
            									if(params.qstring.lastTPullTime && camp.payload.st){
            										if(params.qstring.lastTPullTime < camp.payload.st){
            											retVal.push(camp.payload);
            										}
            									}
            									else{
            										retVal.push(camp.payload);
            									}
                          }
        								});
        								appAndUserInf.activeTransCampaigns = retVal;
        							}else{
        								appAndUserInf.activeTransCampaigns = data;
        							}
        						}
        					});
        				}
        				else{
        					fnCallback();
        				}
        	  }
    			], function(err){
    				common.returnOutput(params,appAndUserInf);
    			});
    }

    returnDefaultFeedbackCampaign = function(startDate){
        if(startDate==undefined || startDate==null){
            startDate = common.getCurrentEpochTime()*1000;
        }
        let obj = {};
        obj._id = "xxxxx";
        obj.tid = "xxxxx";
        obj.t = "FEEDBACK";
        obj.sd = startDate*1000;
        obj.ed = common.getFutureEpochTime(1);
        obj.st = moment().valueOf()-180000; //This is used on the client side to manage fetching of active campaigns and ignore device time if it is wrong.
        return obj;
    }

    let prepareLiveEvents = function (obj, item, isExists){
    	if(isExists){
    		obj.forEach(function(what, key){
    			if(item.aud.what[key].operand == 'install'){
    				item.aud.what[key].process = false; // false mean it will not process on client side
    				item.aud.what[key].state = true; // found event on server
    			}
    			else{
    				item.aud.what[key].process = what.p; // false mean it will not process on client side
    				if(typeof what.s === "boolean"){
    					item.aud.what[key].state = what.s; // found event on server
    					//item.aud.what[key].value = what.s;
    				}
    				else{
    					item.aud.what[key].state = true; // found event on server
    					//item.aud.what[key].value = true;
    				}
    			}

    			// check event segment information
    			if(item.aud.what[key].attr && item.aud.what[key].attr != ''){
    				item.aud.what[key].segment={check:{}}
    				//item.aud.what[key].segment.check[item.aud.what[key].operand] = {op:item.aud.what[key].operator,v:item.aud.what[key].state}
    				item.aud.what[key].segment.check[item.aud.what[key].operand] = {op:item.aud.what[key].operator,v:what.s}
    				//item.aud.what[key].value = true; //item.aud.what[key].value;
    				item.aud.what[key].state = true; //item.aud.what[key].value;
    			}

    			if(item.aud.what[key].attr){
    				delete item.aud.what[key].attr;
    			}``
    		});
    	}
    	else{
    		obj.forEach(function(what){
    			if(what.operand == 'install'){
    				what.process = false; // false mean it will not process on client side
    				what.state = true; // found event on server
    			}
    			else{
    				what.process = true; // false mean it will not process on client side
    				what.state = false; // found event on server
    			}

    			// check event segment information
    			if(what.attr && what.attr != ''){
    				what.segment={check:{}}
    				what.segment.check[what.operand] = {op:what.operator,v:what.value}
    				//   what.value = what.state;
    				//what.value = true; // For Shine.com Fix
    			}

    			if(what.attr){
    				delete what.attr;
    			}
    		});
    	}
    }

    function populatePushObject(template,data, params){
    	data.nh=template.notificationHeader;
    	data.nd=template.notificationDescription;
    	data.ni=template.notificationImage;
    	data.end=template.expandedNotificationDescription;
    	data.eni=template.expandedNotificationImage;
    	data.eurl=template.externalUrl;
    	data.et = template.notificationType;
    	data.actions = template.actions;
    	data.sound = template.sound;
    	data.vibrate = template.vibrate;
    	data.badge = template.badge;
      if(template.cdata){
		let cdataObj = template.cdata
            
		for (let key of Object.keys(cdataObj)) {
			cdataObj[key]  = decodeURIComponent(cdataObj[key])
		}
            if(calculateDataSize(template.cdata)  > common.config.campaignPayloadSize ){
              data.cdata = common.config.host+"/o/templates/getCustomData?app_id="+params.qstring.app_id+"&api_key="+params.qstring.api_key+"&tid="+template._id;
            }
            else{
                data.cdata = cdataObj;
            }
        }
    }
    function calculateDataSize(data){
    	let bytes = 0;
    	let keys = Object.keys(data);
        keys.forEach(function(key){
            bytes += key.length * 2;
            bytes += data[key].length * 2;
        });
        return bytes;
    }

    function populateInAppObject(template,data){
    	data.mode=template.view_mode;
    	data.nh=template.notificationHeader;
    	data.nd=template.notificationDescription;
		data.ni= (template.notificationImage && template.notificationImage != '')? encodeURI(semusiConfig.cdnUrl+"/i/V1/campaignImg?campImg="+template.notificationImage) : template.notificationImage;
    	data.ii=template.iconImage;
		if(template.notificationType){
            data.et = (template.notificationType) ? template.notificationType : template.et;
        }
    	data.aurl=template.actionUrl;
    	data.at=template.actionTitle;
    	data.sa=template.showAction;
		data.cdata = (template.cdata) ? template.cdata : {} ;
		// decoding the values  for each key of cdata 
		if(template.cdata){
            let cdataObj = template.cdata    
            for (let key of Object.keys(cdataObj)) {
                cdataObj[key]  = decodeURIComponent(cdataObj[key])
            }
			data.cdata = cdataObj
        }
	    //in case of deeplink add the action url to the cdata 
		if(data.et == "dl"){
			data.cdata.aurl = data.aurl   
		}
    }
    function populateRatingObject(template,data){
    	data.q=template.question;
    	data.ai=template.activeImage;
    	data.di=template.disabledImage;
    	data.m=template.message;
    	data.at=template.title;
    }

    function populateSurveyObject(template,q){
    	 
    	if(template && template.q){
    		template.q.forEach(function(item){
    			let question={};
    			let options =[];
    			question.id=item.id;
    			question.qt=item.questionText;
    			question.t=item.questionType;
    			question.st=item.sampleText;
    			question.oo=item.otherOption;
    			item.questionOptions.forEach(function(opt){
    				let option={};
    				option.ot= opt.optionText;
    				options.push(option);
    			});
    			question.qo=options;
    			q.push(question);
    		});
    	}
    }

	/**
     * get users CompetingApps app information
     * @param send in request
     * @return array with json data
     **/
    usersApi.getAllCompetingAppsUsers = function(params){

        if( params.qstring.app_id ){
            //let match = {};
               common.db.collection('app_users'+params.qstring.app_id).find({'_custom_UserId': { $exists: true, $not: { '$size': 0 }},"competingapps.apps.name": { $exists : true, $not: {'$size':0 }}},{"competingapps.apps.name":1,"_custom_UserId":1,"_id":0}).skip(28063).limit(5000).toArray(function(err,data){
					let data_with_err = { "err":err, "data":data }; 
					
					let json = data;
					//common.returnOutput(params, data);
					//let comData = appsApi.getCompetingApps(params);
					
					//let fields = Object.keys(json[0]);
					let fields = ["ClientId","Kite","Upstox", "5paisa", "HDFC", "Edelweiss", "Sharekhan", "ICICI", "IIFL", "Motilal Oswal", "Paytm Money"];
					//common.returnOutput(params, fields);
					let replacer = function(key, value) { return value === null ? '' : value } 
					let csv = json.map(function(row){
					return fields.map(function(fieldName){
						let rData = row['competingapps']['apps'];
						if(fieldName == "ClientId"){ 
							return JSON.stringify(row['_custom_UserId'], replacer)
						}else{
							return rData.map(function(name){
								if(name.name == fieldName){
									return JSON.stringify("1", replacer)
								}else{
									return JSON.stringify("0", replacer)
								}
							})
						}
						
					}).join(',')
					})
					csv.unshift(fields.join(',')) // add header column
					logger.info(`${csv.join('\r\n')}`) // add header column
					//common.returnCSV(params, json2csv.convert2csv(csvArray, false).csv);
					//common.returnOutput(params, csv.join('\r\n'));
					//returnMessage(params,200,csv.join('\r\n'));
					params.res.write(csv.join('\r\n'));
					//common.returnOutput(params, csv.join('\r\n'));
                    
                })
        }
        else{
            common.returnOutput(params,{});
        }
	}


/**
 * Fetch site settings from the database based on the server domain and send the configuration along with members data as output.
 *
 * @param {Object} params - Parameters object containing necessary data for processing
 * @param {Array} members - Array of member data
 */
	function fetchAndSendConfigWithOutput(params, members) {

		// Retrieve site settings from the database based on the server domain
		common.db.collection('site_settings').findOne({ "cdn": semusiConfig.serverDomain }, (err, sitesettings) => {
		
			// Handle database error
			if (err) {
				common.returnOutput(params, { members });
			} else {
				// Prepare data with members and optional site configuration
				const data = {
					members: members,
					siteConfig: sitesettings || {} // Default to empty object if sitesettings is null or undefined
				};
				// Send output data using common returnOutput function
				common.returnOutput(params, data);

			}
	

		})
	}
    // validate delayed campaign
    function validateDelayedCampaign(campaign, params, callback){
    			// get delayed time into sec
    			let delayedSec = (campaign.delayu == "Hours")? (parseInt(campaign.delayi)*60)*60 : parseInt(campaign.delayi)*60
				logger.info(`delayedSec => ${delayedSec}`)
    			// check aud segment is exists or not in campaign
    			if(campaign.aud && campaign.aud.what && campaign.aud.what.length > 0){
    				// get user et informaiton from cache
    				campaignCommon.validateCampaignUser(params.qstring.app_id, campaign._id, params.qstring.device_id, function(error, isValidate, uData){
						logger.error(`validateDelayedCampaign error ==>>> ${error}`)
						logger.info(`${params.qstring.device_id}::isValidate, uData >> ${isValidate}::${uData} `)
    					// validate user data exists or not into cache
    					if(uData && uData.e && uData.e[0].et){
    						// get delayed time into sec
    						let userTime = delayedSec + parseInt(uData.e[0].et)
							logger.info(`${params.qstring.device_id} \n userTime ${userTime} <-= ${(parseInt(params.qstring.currentTime/1000)+60)}`)
    						// validate user current access time and event time + delay time
    						if(userTime <= (parseInt(params.qstring.currentTime/1000)+60) ){
    							// remove user from cache after get send campaign
    							cacheApi.client.hdel("campaignuser_"+params.qstring.app_id+""+campaign._id, params.qstring.device_id, function(error, delRes){
									logger.error(`error==>> ${error}`)
									logger.info(`delete campaignuser_ ${params.qstring.app_id} :${campaign._id}::delres ${delRes} `)	 
    							})
    							return callback(null, uData, false)
    						}
    						else{
    							return callback('current time is greter then user event and dealyed', null, false)
    						}
    					}
    					else{
    						return callback('data not Found', null, false)
    					}
    				});
    			}
    			else{
    				// convert delayed time into miliseconds
    				delayedSec = parseInt(delayedSec)*1000
    				// add delayed time and campaign update time
    				let campTime = parseInt(campaign.ud) + parseInt(delayedSec);
    				// compare campaign update time + delayed time with user current access time
    				if( campTime <= (parseInt(params.qstring.currentTime)+60*1000) ) {
    					cacheApi.client.hget("deliver_user_"+params.qstring.app_id+campaign._id, params.qstring.device_id, function(error, cacheData){
							logger.error(`error=> ${error}`);
							logger.info(`${params.qstring.device_id}  = deliver_user_ , cacheData  :: +> ${cacheData}`);
    						if(!error && cacheData == null){
    							cacheApi.client.hset("deliver_user_"+params.qstring.app_id+campaign._id, params.qstring.device_id,  "sent at "+params.qstring.currentTime, function(){

    							});
    							return callback(null, null, true)
    						}
    						else{
    							return callback(null, null, false)
    						}
    					});
    				}
    				else{
    					return callback(null, null, false)
    				}
    			}
    		}

}(usersApi));

module.exports = usersApi;
