var templateApi = {},
    common = require('./../../utils/common.js'),
    campaignDb = require('./../../utils/campaign.db.js'),
    dateFunctions = require('./../../utils/date.js'),
    {HandlebarsContentEngine} = require('src_typescript_appice-core/dist/channels/handlebars/handlebarsContentEngine.js')
    logger = require('../../../logger'),
    campaignCommon = require('./../../utils/campaign.common.js'),
    apnPush = require('./../../utils/semusi.apndirectpush.js'),
    fs = require('fs'),
    path = require('path'),
    request = require('request'),
    xss = require("xss"),
    moment = require('moment'),
    gcm = require('node-gcm'),
    mail = require('./mail.js'),
    validate = require('../../constants/constants.js'),
    apn = require('apn'),
    ErrorMsg = require('../../constants/constants'),
    semusiConfig = require('./../../config'),
    async = require('async'),
        _ = require('underscore'),
        cacheApi = require('./../../utils/semusi.cache.js'),
        commonEvent = require('./../../utils/common.event.js'),
        campaignUtils = require('./../../utils/campaign.utils'),
        dataProducer = require('./../../utils/common.data.producer.js'),
        apn = require('apn'),
        crypto = require('crypto'),
        executeCamp = require('./execute-camp');
const webPush = require('web-push');
const errorHandler = require('./../../utils/error.handler.js');
const postback = require('./../../ref_plugins/postback_url.js');
const psqlUtils = require('./../../utils/pgsql.utils.js');
const uuid = require('uuid');
const https = require('https');


(function(templateApi) {


//To search transactional campaigns by requestId
templateApi.getRequestId = async function (params) {
    let request_id = params.qstring.requestId;
    let app_id = params.qstring.app_id;

    const query = `
        SELECT 
            payload, 
            to_char((createdat AT TIME ZONE 'Asia/Kolkata' AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Dubai'), 'YYYY-MM-DD HH24:MI:SS') AS createdat
        FROM campaigntransactional_${app_id}_0
        WHERE (payload::jsonb)->'camp_data'->'custom'->>'requestId' = '${request_id}'
        UNION ALL
        SELECT 
            payload, 
            to_char((createdat AT TIME ZONE 'Asia/Kolkata' AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Dubai'), 'YYYY-MM-DD HH24:MI:SS') AS createdat
        FROM campaigntransactional_${app_id}_1
        WHERE (payload::jsonb)->'camp_data'->'custom'->>'requestId' = '${request_id}'
        UNION ALL
        SELECT 
            payload, 
            to_char((createdat AT TIME ZONE 'Asia/Kolkata' AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Dubai'), 'YYYY-MM-DD HH24:MI:SS') AS createdat
        FROM campaigntransactional_${app_id}_2
        WHERE (payload::jsonb)->'camp_data'->'custom'->>'requestId' = '${request_id}';
    `;

    try {
        let result = await psqlUtils.runQuery(query);
        logger.info("Result from searching requestID:", JSON.stringify(result));

        let simplifiedData = [];

        result.forEach(item => {
            if (Array.isArray(item.rows)) {
                item.rows.forEach(row => {
                    if (row.payload && row.payload.camp_data && row.payload.camp_data.custom && row.payload.camp_data.custom.requestId) {
                        let data = {
                            requestId: row.payload.camp_data.custom.requestId,
                            CampaignID: "", // You can set this value as needed
                            Timestamp: row.createdat // Adding the Timestamp property
                        };
                        simplifiedData.push(data);
                    }
                });
            } else {
                logger.error('Unexpected data format in result:', item);
            }
        });
        common.returnOutput(params, simplifiedData);
    } catch (error) {
        logger.error('Error executing query:', error);
        common.returnMessage(params, `Error executing query: ${error.message}`);
    }
};
   templateApi.getShortUrls = function (params){
       common.db.collection('campaigns_' + params.qstring.app_id).find(
           { 'url': { $exists: true } },
           { '_id': 0, 'url': 1 }
       ).toArray(function (err, results) {
           if (err) {
               logger.error("err", err);
           } else {
               if (results.length > 0) {
                   let urls = results.map(doc => doc.url);
                   common.returnOutput(params, urls)
               } else {
                   logger.error("No 'url' keys found in the document.");
               }
           }
       });      
   }
       /**
     * getDataCampaignStatsDaywise method returns Object  
     * @param {*} array of objects where each object represent the data of one month and the month will have day wise and hour wise
     * @returns the Object of Platforms and inside them will be day wise data only which means hours wise will be in sum
     */
       const getDataCampaignStatsDaywise = function(inputDataArray, sd, ed){

        // Turn epochs in the format of YYYY_MM_DD
        const startDate = dateFunctions.convertEpochToFormattedDate(sd)
        const endDate = dateFunctions.convertEpochToFormattedDate(ed)

        const result = {};
        const platforms = ["android", "ios", "web"];
        inputDataArray.forEach((inputData) => {
          platforms.forEach((platform) => {
            if (inputData.hasOwnProperty(platform)) {
              if (!result.hasOwnProperty(platform)) {
                result[platform] = {};
              }
      
              for (const date in inputData[platform]) {
               if (dateFunctions.isDateInRange(date, startDate, endDate)) {
                if (inputData[platform].hasOwnProperty(date)) {
                  const dateSum = result[platform][date] || {};
      
                  for (const hour in inputData[platform][date]) {
                    if (inputData[platform][date].hasOwnProperty(hour)) {
                      const hourData = inputData[platform][date][hour];
      
                      for (const key in hourData) {
                        if (hourData.hasOwnProperty(key)) {
                          if (dateSum.hasOwnProperty(key)) {
                            dateSum[key] += hourData[key];
                          } else {
                            dateSum[key] = hourData[key];
                          }
                        }
                      }
                    }
                  }
      
                  result[platform][date] = dateSum;
                }
                }
              }
            }
          });
        });
      
        return result;
      }
    /**
     * sumPlatformData 
     * This function takes an array of documents containing data for different platforms and 
     * aggregates the data based on platform, summing up values for each key.
     * @param {Array} data - An array of documents containing platform data.
     * @returns {Array} - An array of objects containing aggregated data for each platform.
     */
    const sumPlatformData = function (data) {
        let result = [];

        // Extract platform names
        let platforms = data.reduce((platforms, document) => {
            return platforms.concat(Object.keys(document));
        }, []);
        platforms = [...new Set(platforms)]; // Unique platform names

        // Remove _id field if exists
        platforms = platforms.filter(platform => platform !== '_id');


        // Iterate over platforms
        platforms.forEach(platformName => {
            let platformData = {
                "platformName": platformName,
            };

            // Iterate over documents
            data.forEach(document => {
                if (document[platformName]) {
                    let platform = document[platformName];

                    // Iterate over dates
                    Object.values(platform).forEach(date => {
                        // Iterate over hours
                        Object.values(date).forEach(hour => {
                            // Iterate over keys and sum them up
                            Object.keys(hour).forEach(key => {
                                platformData[key] = (platformData[key] || 0) + (hour[key] || 0);
                            });
                        });
                    });
                }
            });

            result.push(platformData);
        });

        return result;
    }
   
    templateApi.getCampaignStatsDateWise = async function (params) {
            let argProps = {
                'startDate':{
                    'required': true,
                    'type': 'String'
                },
                'endDate':{
                    'required': true,
                    'type': 'String'
                },
                'cid': {
                    'required': true,
                    'type': 'String'
                } 
    
          
            },
            newTemplate = {};
    
            if (!(newTemplate = common.validateArgs(params.qstring, argProps))) {
                common.returnMessage(params, 400, 'Not enough args');
                return false;
            }
    

            // get the app settings 
            const app = await common.retrieveAppSettings(params.qstring.app_id);

            //get default columns
            const DEFAULT_COLUMN = validate.getColumns("DEFAULT_COLUMN")

            // Convert string into formatt
            const startDateInFormat =  dateFunctions.ConvertEpochtoCommonFormatDate(params.qstring.startDate.toString())
            const endDateInformat   =  dateFunctions.ConvertEpochtoCommonFormatDate(params.qstring.endDate.toString())
            // get the _ids for the campids 
            const array = common.getCampidnAppidwithDates(params.qstring.app_id, params.qstring.cid.toString(), startDateInFormat, endDateInformat);



            // get the campaign object from statsData collection
            const campaign = await campaignDb.retrieveCampDataFromStatsData(params.qstring.app_id, params.qstring.cid.toString())

            // get the campaign object from timely_data collection
            const campDetails = await campaignDb.retrieveCampDataFromTimely_campaign_statsdata(array)

            

            //get column configuration if it is in app , else use default
            campaign.columnStatus = (app.campaign_columns) ? app.campaign_columns : DEFAULT_COLUMN

            //by default appInbox would be disabled 
            campaign.appInboxreports = false

            // if it is enabled set it as true
            if(app.features && app.features.appInboxReports != undefined && app.features.appInboxReports == false){
                campaign.appInboxreports = true
            }


            if(campDetails){
            
                // get daywiseData
                const daywiseData = getDataCampaignStatsDaywise(campDetails ,params.qstring.startDate.toString(), params.qstring.endDate.toString());
                      
                //get platformwise data
                const platformWiseData = sumPlatformData(campDetails)

                // process & sum campaign data of timely 
                const camp_timely_data = aggregateCampaignDataByDate(campDetails, params.qstring.startDate.toString(), params.qstring.endDate.toString())

                  // merge processed data of timely with campaign statsdata 
                const camp_merged_data = mergeCampaignObjects(camp_timely_data, [campaign])

                // Process the entire campaign object
                const finalCampaignData = await prepareCampaignDetails(params.qstring.cid.toString(), camp_merged_data[0])

                // return the object
                common.returnOutput(params, {daywiseData,platformWiseData, campaign :finalCampaignData});
            }else {

                const finalCampaignData = await prepareCampaignDetails(params.qstring.cid.toString(), campaign)

                  // return the object
                  common.returnOutput(params, { campaign :finalCampaignData});
            }
      }
    
    /** extractVariables creates the instance of HandlebarsContentEngine ts class 
        and calls the extractMessageVariables function which return the Mustache 
        variables from the payload
    **/
        let extractVariables = function(payload){
             const handleBarVariables = new HandlebarsContentEngine()
             return handleBarVariables.extractMessageVariables(payload)
         }
     
         /** addParsedKeysToTemplate function adds Mustache variables into the templates
          * as array of srings  
         **/
         let addParsedKeysToTemplate = function(newTemplate){
             const parsedKeys = [];
             parsedKeys.push(extractVariables(newTemplate.template.bodyHtmlData))
             parsedKeys.push(extractVariables(newTemplate.template.subjectData))
             newTemplate.template['parsedKeys'] = parsedKeys.flat(1);
         }
         
         templateApi.createTemplate = function(params) {
            let argProps = {
                    'template_name': {
                        'required': true,
                        'type': 'String'
                    },
                    'template_type': {
                        'required': false,
                        'type': 'String'
                    },
                    'template': {
                        'required': false,
                        'type': 'JSON'
                    },
                    'isTemplateDeleted': {
                        'required': false,
                        'type': 'String'
                    },
                    'createdOn': {
                        'required': false,
                        'type': 'String'
                    },
                    'modifiedOn': {
                        'required': false,
                        'type': 'String'
                    }
                },
                newTemplate = {};
        
            try {
                if (!(newTemplate = common.validateArgs(typeof params.qstring.args != 'object' ? JSON.parse(params.qstring.args) : params.qstring.args, argProps))) {
                    logger.error('Validation error: Invalid arguments while trying to create template');
                    common.returnMessage(params, 422, 'Validation error');
                    return false;
                }
        
                newTemplate['createdAt'] = common.getCurrentEpochTime();
                logger.info('Creating new template', { template: newTemplate });
        
                if (newTemplate.template_type == 'EMAIL') {
                    logger.info('Template type is EMAIL, parsing keys');
                    addParsedKeysToTemplate(newTemplate);
                }
        
                common.db.collection('templates_' + params.qstring.app_id).insert(newTemplate, function(err, tmpl) {
                    if (err) {
                        logger.error('Database insertion error', { error: err });
                        common.returnMessage(params, 500, 'Database error');
                        return;
                    }
        
                    if (tmpl && tmpl.ops) {
                        newTemplate._id = tmpl.ops[0]._id;
                        logger.info('Template created successfully', { templateId: newTemplate._id });
                    } else {
                        logger.error('Template insertion succeeded, but no ops returned');
                    }
        
                    common.returnOutput(params, newTemplate);
                });
            } catch (error) {
                logger.error('An unexpected error occurred', { error: error });
                common.returnMessage(params, 500, 'An unexpected error occurred');
            }
        };

    templateApi.getIEmailEditor = async function(params){
        try {
            const config = {
                client_id : semusiConfig.AIEmailEditor.client_id, 
                client_secret : semusiConfig.AIEmailEditor.client_secret,
                grant_type: semusiConfig.AIEmailEditor.grant_type
            };
            const url = semusiConfig.AIEmailEditor.endpoint
          await request.post({url, form : config}, function(err, httpResponse, body){
            common.returnOutput(params, body);
           }) 
        } catch (error) {
            logger.error(`error in getIEmailEditor ${error}`)
            common.returnMessage(params, 400, 'something went wrong');
            return false;
        }

    }

    templateApi.updateTemplate = function(params) {
        let argProps = {
                'template_name': {
                    'required': true,
                    'type': 'String'
                },
                'template_type': {
                    'required': false,
                    'type': 'String'
                },
                'template': {
                    'required': false,
                    'type': 'JSON'
                },
                'isTemplateDeleted': {
                    'required': false,
                    'type': 'String'
                },
                'modifiedOn': {
                    'required': false,
                    'type': 'String'
                }
            },
            updatedTemplate = {};

        if (!(updatedTemplate = common.validateArgs(typeof params.qstring.args != 'object' ? JSON.parse(params.qstring.args): params.qstring.args, argProps))) {
            common.returnMessage(params, 422, 'Validation error');
            return false;
        }

        updatedTemplate['updatedAt'] = common.getCurrentEpochTime();
        if(updatedTemplate.template_type == 'EMAIL') addParsedKeysToTemplate(updatedTemplate)
        common.db.collection('templates_' + params.qstring.app_id).update({
            '_id': common.db.ObjectID(params.qstring.template_id)
        }, {
            $set: updatedTemplate
        }, {
            safe: true
        }, function(err, isOk) {
            if (!err) {
                common.db.collection('templates_' + params.qstring.app_id).findOne({
                    '_id': common.db.ObjectID(params.qstring.template_id)
                }, function(err, updatedTemplate) {
                    if (updatedTemplate && !err) {


                        common.returnOutput(params, updatedTemplate);
                    } else {
                        logger.error(err);
                        common.returnMessage(params, 500, 'Error updating template');
                    }
                });
            } else {
                logger.error("there is an error, the set template was " + updatedTemplate + ' error is ' + err);
            }
        });

        return true;
    };

    templateApi.deleteTemplate = function(params) {
        let argProps = {
                'isTemplateDeleted': {
                    'required': false,
                    'type': 'String'
                },
                'modifiedOn': {
                    'required': false,
                    'type': 'String'
                }
            },
            updatedTemplate = {};

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

        updatedTemplate['updatedAt'] = common.getCurrentEpochTime();
        common.db.collection('templates_' + params.qstring.app_id).update({
            '_id': common.db.ObjectID(params.qstring.template_id)
        }, {
            $set: updatedTemplate
        }, {
            safe: true
        }, function(err, isOk) {
            if (!err) {
                common.db.collection('templates_' + params.qstring.app_id).findOne({
                    '_id': common.db.ObjectID(params.qstring.template_id)
                }, function(err, updatedTemplate) {
                    if (updatedTemplate && !err) {


                        common.returnMessage(params, 200, 'Success');
                    } else {
                        common.returnMessage(params, 500, 'Error updating template');
                    }
                });
            }
        });

        return true;
    };

    templateApi.getTemplates = function(params) {
        common.db.collection('templates_' + params.qstring.app_id).find({
            "isTemplateDeleted": "false"
        }).toArray(function(err, result) {
            if (!result) {
                result = {};
            }
            common.returnOutput(params, result);
        });
    };

    templateApi.getCampaigns = function(params) {
        common.db.collection('campaigns_' + params.qstring.app_id).find({
            "d": false
        }).toArray(function(err, result) {
            if (!result) {
                result = {};
            }
            common.returnOutput(params, result);
        });
    };

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

    updateCampaignStatus = function(id, currentDate) {
        currentDate = moment.utc().hours(0).minutes(0).seconds(0).milliseconds(0).valueOf();
        let updatedAt = common.getCurrentEpochTime();

        // delete active and draft campaign when data goes expire
        common.db.collection('campaigns_' + id).find({
            "d": false,
            $or: [{
                "st": "ACTIVE"
            }, {
                "st": "DRAFT"
            }],
            "ed": {
                $lt: currentDate
            }
        }).toArray(function(err, result) {
            if (!err && result) {
                result.forEach(function(cmp) {
                    try {
                        cmp.segmentinfo = JSON.parse(cmp.segmentinfo);
                    } catch (e) {
                        logger.error(`segmentinfo : ${e.message}, stack: ${e.stack}`);
                    }

                    // remove memeber form delayed_campaigns list
                    cacheApi.removeMember("delayed_campaigns", id + "_" + cmp._id, function(error, result) {
                        if (error) {
                            logger.error("Error while remove delayed_campaigns member in template")
                        }
                        // remove campaign data key
                        cacheApi.deleteKey(id + "_" + cmp._id, function(error, response) {
                            logger.info("error, response  in template file ", error, "  == ", response);
                        });
                    });

                    // delete key if campaign is in draft
                    if (cmp.segmentinfo && cmp.segmentinfo.what && cmp.segmentinfo.what > 0) {
                        if (cmp.delays && cmp.segmentinfo.what.length == 1) {
                            cmp.segmentinfo.what.forEach(function(evt) {
                                cacheApi.deleteKey(evt.operand + "_" + id, function() {

                                });
                            });
                        }

                        // delete cache user hmset,
                        cacheApi.deleteKey("campaignuser_" + id + "" + cmp._id, function() {

                        });
                    }

                    //delete campaign user cache
                    cacheApi.deleteKey("campaignuser_" + id + "" + cmp._id, function(err, res) {
                        logger.info("Redis key cleared. " + "campaignuser_" + id + "" + cmp._id);
                    });
                    //delete campaign event cache
                    cacheApi.deleteKey("campaignevent_" + id + "" + cmp._id, function(err, res) {
                        logger.info("Redis key cleared. " + "campaignevent_" + id + "" + cmp._id);
                    });

                    //delete campaign event cache
                    cacheApi.deleteKey("deliver_user_" + id + "" + cmp._id, function(err, res) {
                        logger.info("Redis key cleared. " + "campaignevent_" + id + "" + cmp._id);
                    });
                });
            }
        });

        common.db.collection('campaigns_' + id).update({
            "d": false,
            $or: [{
                "st": "ACTIVE"
            }, {
                "st": "DRAFT"
            }],
            "ed": {
                $lt: currentDate
            }
        }, {
            "$set": {
                "st": "PAST",
                'updatedAt': updatedAt
            }
        }, {
            multi: true
        }, function(err, result) {
            logger.info("updateCampaignStatus result " + JSON.stringify(result))
        });
    }

    // get transitional campaigns
    templateApi.getTransitionalCampaigns = function(params) {
        let argProps = {
                'startDate': {
                    'required': true,
                    'type': 'Number'
                },
                'endDate': {
                    'required': false,
                    'type': 'Number'
                },
                'offset': {
                    'required': false,
                    'type': 'Number'
                }
            },
            period = {};

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

        let result = {};
        let audId = "";
        let index = 1;
        let retVal = [];
        let sortlist = ['_id', 'name'];
        let sortBy = {
            'createdAt': -1
        };
        // check order by column
        if (params.qstring["order[0][column]"] ||
            (params.qstring["order[0][column]"] && params.qstring["order[0][column]"] == 0)) {

            sortBy = {};
            // check filter column
            if (params.qstring["order[0][column]"] < 2) {
                sortBy[sortlist[params.qstring["order[0][column]"]]] = (params.qstring["order[0][dir]"] == 'asc') ? 1 : -1;
            } else {
                sortBy['createdAt'] = (params.qstring["order[0][dir]"] == 'asc') ? 1 : -1;
            }
        }

        //match
        common.db.collection('transaction_camp_' + params.qstring.app_id).find({}).sort(sortBy).skip(parseInt(params.qstring.start)).limit(parseInt(params.qstring.length)).toArray(function(err, campaigns) {
            if (err) {
                logger.error(err);
                common.returnOutput(params, retVal);
                return false;
            }
            async.forEach(campaigns, function(item, callback) {
                item.campid = item._id;
                delete item._id;
                item.timestamp = parseInt(item.createdAt) + parseInt(period.offset);
                item.pushedTo = 0;
                item.viewed = 0;
                item.clicked = 0;
                item.deleted = 0;
                item.reach = (item.gcmids) ? item.gcmids.length : ((item.dids) ? item.dids.length : ((item.users) ? item.users.length : 0));
                item.head = (item.payload.nh) ? item.payload.nh : '';
                item.desc = (item.payload.nd) ? item.payload.nd : '';;
                delete item.createdAt;
                item.dnr = 0;
                item.userPushCount = 0;
                item.userPushCount += (item.fcm) ? item.fcm.pushedTo : 0;
                item.userPushCount += (item.gcm) ? item.gcm.pushedTo : 0;
                item.pushedTo = item.userPushCount;
                async.parallel([
                    function(callback) {
                        common.db.collection('events_stats_transaction').find({
                            '_id': params.qstring.app_id + item.campid
                        }).toArray(function(err, campStats) {
                            if (err) {
                                logger.error('err:' + JSON.stringify(err));
                                callback();
                            } else {
                                if (campStats) {
                                    let cStats = getCampaignsStats(campStats).stats;
                                    if (cStats['Campaign_Received']) {
                                        item.pushedTo = (item.userPushCount > cStats['Campaign_Received']) ? item.userPushCount : cStats['Campaign_Received'];
                                    }

                                    if (cStats['Campaign_Viewed']) {
                                        item.viewed = cStats['Campaign_Viewed'];
                                    }
                                    if (cStats['Campaign_Deleted']) {
                                        item.deleted = cStats['Campaign_Deleted'];
                                        item.dnr = item.deleted;
                                    }
                                    if (cStats['Campaign_Clicked']) {
                                        item.clicked = cStats['Campaign_Clicked'];
                                    }
                                    //item.ctr = parseFloat(item.clicked/item.pushedTo*100).toFixed(2)+"%";
                                }
                                callback();
                            }
                        });
                    }
                ], function(err) {
                    if (err) {
                        callback(err);
                    } else {
                        // if(item.reach!='NA' && (item.reach<item.pushedTo)){
                        //     item.reach = item.pushedTo;
                        // }
                        retVal.push(item);
                        callback();
                    }

                });
            }, function(error) {
                common.db.collection('transaction_camp_' + params.qstring.app_id).count({}, function(err, count) {
                    if (count) {
                        let obj = {
                            "draw": parseInt(params.qstring.draw),
                            "recordsTotal": count,
                            "recordsFiltered": count,
                            "data": retVal
                        }
                    } else {
                        let obj = {
                            "draw": parseInt(params.qstring.draw),
                            "recordsTotal": campaigns.length,
                            "recordsFiltered": campaigns.length,
                            "data": retVal
                        }
                    }
                    common.returnOutput(params, obj);
                });
            });
        });
    };

    // download transitional campaigns
    templateApi.downloadTransitionalCampaigns = function(params) {
        let argProps = {
                'startDate': {
                    'required': true,
                    'type': 'Number'
                },
                'endDate': {
                    'required': false,
                    'type': 'Number'
                },
                'offset': {
                    'required': false,
                    'type': 'Number'
                }
            },
            period = {};

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

        let csvData = "Campaign ID, Title, Reachable, Received, Viewed, Ignored, Clicked, Date, Devices";
        let result = {};
        let audId = "";
        let index = 1;
        let retVal = [];
        let match = {
            createdAt: {
                $gte: parseInt(period.startDate * 1000),
                $lte: parseInt(period.endDate * 1000)
            }
        }
        let sortlist = ['_id', 'name'];
        let sortBy = {
            'createdAt': -1
        };
        // check order by column
        if (params.qstring["order[0][column]"] ||
            (params.qstring["order[0][column]"] && params.qstring["order[0][column]"] == 0)) {

            sortBy = {};
            // check filter column
            if (params.qstring["order[0][column]"] < 2) {
                sortBy[sortlist[params.qstring["order[0][column]"]]] = (params.qstring["order[0][dir]"] == 'asc') ? 1 : -1;
            } else {
                sortBy['createdAt'] = (params.qstring["order[0][dir]"] == 'asc') ? 1 : -1;
            }
        }

        common.db.collection('transitional_campaigns_aggregate_' + params.qstring.app_id).find(match).sort(sortBy).toArray(function(err, campaigns) {
            if (err) {
               logger.error(err);
                common.returnOutput(params, retVal);
                return false;
            }

            async.forEach(campaigns, function(item, callback) {
                item.campid = item._id;
                delete item._id;
                item.timestamp = moment(item.createdAt + parseInt(period.offset)).format('MM/DD/YYYY HH:mm:ss');
                item.pushedTo = 0;
                item.pushed = 0;
                item.viewed = 0;
                item.clicked = 0;
                item.deleted = 0;
                //item.segmentName = "";
                item.reach = (item.devices) ? item.devices.length : 0;
                item.devices = (item.devices) ? item.devices.toString() : '';
                //item.segmentinfo="";
                item.dnr = 0;
                if (item.devices) {
                    item.devices = item.devices.replace(/\,/g, " : ");
                }
                item.userPushCount = 0;
                item.userPushCount += (item.fcm) ? item.fcm.pushedTo : 0;
                item.userPushCount += (item.gcm) ? item.gcm.pushedTo : 0;
                item.pushedTo = item.userPushCount;
                //item.ctr=0;
                async.parallel([
                    function(callback) {
                        common.db.collection('events_stats').find({
                            '_id': params.qstring.app_id + item.campid
                        }).toArray(function(err, campStats) {
                            if (err) {
                                logger.error('err:' + JSON.stringify(err));
                                callback();
                            } else {
                                if (campStats) {
                                    let cStats = getCampaignsStats(campStats).stats;
                                    if (cStats['Campaign_Received']) {
                                        item.pushedTo = (item.userPushCount < cStats['Campaign_Received']) ? item.userPushCount : cStats['Campaign_Received'];
                                    }

                                    if (cStats['Campaign_Viewed']) {
                                        item.viewed = cStats['Campaign_Viewed'];
                                    }
                                    if (cStats['Campaign_Deleted']) {
                                        item.deleted = cStats['Campaign_Deleted'];
                                        item.dnr = item.deleted;
                                    }
                                    if (cStats['Campaign_Clicked']) {
                                        item.clicked = cStats['Campaign_Clicked'];
                                    }
                                }
                                callback();
                            }
                        });
                    },
                    function(callback) {
                        if (item.broadcast) {
                            let timestamp = moment(item.timestamp).valueOf() / 1000;
                            common.db.collection('app_users' + params.qstring.app_id).count({
                                fs: {
                                    $lt: timestamp + parseInt(period.offset)
                                },
                                active: true,
                                pushyid: {
                                    $ne: null
                                }
                            }, function(err, users) {
                                if (err) {
                                    logger.error('err:' + JSON.stringify(err));
                                    callback();
                                } else {
                                    item.reach = users;
                                    callback();
                                }
                            });
                        } else {
                            callback();
                        }
                    }
                ], function(err) {
                    if (err) {
                        callback(err);
                    } else {
                        // if(item.reach!='NA' && (item.reach<item.pushedTo)){
                        //     item.reach = item.pushedTo;
                        // }

                        csvData += "\r\n" + item.campid + "," + item.name + "," + item.reach + "," + item.pushedTo + "," + item.viewed + "," + item.dnr + "," + item.clicked + "," + item.timestamp + "," + item.devices;
                        callback();
                    }

                });
            }, function(error) {
                common.returnCSV(params, csvData);
            });
        });
    };

    // get campaign details information
    templateApi.getCampaignDetails = function(params) {
        if (params.qstring.campid) {
            common.db.collection('events_stats').find({
                '_id': params.qstring.app_id + params.qstring.campid
            }).toArray(function(err, campStats) {
                if (err) {
                    logger.error('err:' + JSON.stringify(err));
                    callback();
                } else {
                    if (campStats) {
                        let offset = (params.qstri1ng.offset) ? params.qstring.offset : 0;
                        let dataObj = {};
                        dataObj.stats = getCampaignsStats(campStats).objStats;
                        dataObj.stats.c = 0;
                        dataObj.campaign = {}
                        // check campaign type
                        if (params.qstring.type == 'transitional') {
                            common.db.collection('transitional_campaigns_aggregate_' + params.qstring.app_id).findOne({
                                _id: params.qstring.campid
                            }, function(err, campaigns) {
                                if (err) {
                                    logger.error('error occured ' + JSON.stringify(err));
                                    common.returnOutput(params, dataObj);
                                } else {
                                    if (campaigns) {
                                        dataObj.campaign.name = campaigns.name;
                                        dataObj.campaign.desc = (campaigns.desc) ? campaigns.desc : '';
                                        dataObj.campaign.reachTo = (campaigns.devices) ? campaigns.devices.length : 0;
                                        dataObj.campaign.timestamp = parseInt(campaigns.timestamp) + parseInt(offset);
                                        if (dataObj.campaign.reachTo == 0) {
                                            let createdAt = moment(parseInt(campaigns.createdAt) + parseInt(offset)).valueOf() / 1000;
                                            common.db.collection('app_users' + params.qstring.app_id).count({
                                                fs: {
                                                    $lt: createdAt
                                                },
                                                active: true
                                            }, function(err, users) {
                                                if (err) {
                                                    logger.error('err:' + JSON.stringify(err));
                                                } else {
                                                    dataObj.campaign.reachTo = users;
                                                }
                                                common.returnOutput(params, dataObj);
                                            });
                                        } else {
                                            common.returnOutput(params, dataObj);
                                        }
                                    } else {
                                        common.returnOutput(params, dataObj);
                                    }
                                }
                            });
                        } else if (params.qstring.type == 'engagement') {
                            common.db.collection('campaigns_' + params.qstring.app_id).findOne({
                                _id: common.db.ObjectID(params.qstring.campid)
                            }, function(err, campaigns) {
                                if (err) {
                                    logger.error(err);
                                    common.returnOutput(params, {});
                                    return false;
                                }

                                if (campaigns) {
                                    let item = campaigns;
                                    dataObj.campaign.reachTo = 0;
                                    dataObj.campaign.timestamp = parseInt(item.sd);
                                    if (item.aud == undefined || item.aud == "") {
                                        audId = common.db.ObjectID();
                                    } else {
                                        audId = common.db.ObjectID(item.aud);
                                    }
                                    async.parallel([
                                        function(callback) {
                                            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) {
                                                        if (audience.range) {
                                                            dataObj.campaign.reachTo = audience.range;
                                                        } else {
                                                            dataObj.campaign.reachTo = 'NA';
                                                        }

                                                    }
                                                    callback();
                                                }
                                            });
                                        },
                                        function(callback) {
                                            common.db.collection('templates_' + params.qstring.app_id).findOne({
                                                "isTemplateDeleted": "false",
                                                "_id": common.db.ObjectID(item.tid)
                                            }, function(err, tool) {
                                                if (err) {
                                                    callback(err);
                                                } else {
                                                    if (tool && item.d == false) {
                                                        if (item.t) {
                                                            tool.template._id = tool._id;
                                                            dataObj.campaign.template = tool.template;
                                                        }
                                                    }
                                                    callback();
                                                }
                                            });
                                        },
                                        function(callback) { // get conversion from data from keen
                                            if (item.c_event && item.c_event != '') {
                                                let c_milliseconds = ((item.c_period * 60) * 60) * 1000;
                                                let startTime = moment(dataObj.campaign.timestamp).toDate();
                                                let endTime = moment(dataObj.campaign.timestamp + parseInt(c_milliseconds)).toDate();
                                                // funnel query
                                                let steps = [{
                                                    "event_collection": "event_" + params.qstring.app_id + "_Campaign_Clicked",
                                                    "with_actors": false,
                                                    "actor_property": "user_id",
                                                    "timeframe": {
                                                        "start": startTime,
                                                        "end": endTime
                                                    },
                                                    "filters": [{
                                                        "operator": "eq",
                                                        "property_name": "campid",
                                                        "property_value": params.qstring.campid.toString()
                                                    }]
                                                }, {
                                                    "event_collection": "event_" + params.qstring.app_id + "_" + item.c_event,
                                                    "with_actors": false,
                                                    "actor_property": "user_id",
                                                    "timeframe": {
                                                        "start": startTime,
                                                        "end": endTime
                                                    },
                                                    "filters": []
                                                }];
                                                // QUERY TO KEEN
                                                keen.getFunnelResults(steps, function(data) {
                                                    if (data) {
                                                        dataObj.stats.c = (data[1]) ? data[1] : 0;
                                                    }
                                                    callback();
                                                });
                                            } else {
                                                callback();
                                            }
                                        }
                                    ], function(err) {
                                        if (err) {
                                            common.returnOutput(params, {});
                                        } else {
                                            common.returnOutput(params, dataObj);
                                        }
                                    });
                                } else {
                                    common.returnOutput(params, {});
                                }
                            });
                        } else if (params.qstring.type != 'engagement' && params.qstring.type != 'transitional') {
                            common.returnOutput(params, {});
                        }
                    } else {
                        common.returnOutput(params, {});
                    }
                }
            });
        }
    }
  
    templateApi.initiateCampaignExports = function(params) {
        let argProps = {
            'cids': {
                'required': true,
                'type': 'Array'
            },
            'startDayEpoch':{
                'required': true,
                'type': 'Number'
            },
            'endDayEpoch':{
                'required': true,
                'type': 'Number'
            }
        }
        let details
        
        if (!(details = common.validateArgs(params.qstring.args, argProps))) {
            common.returnMessage(params, 400, 'Not enough args');
            return false;
        }
        
        
        if(params.qstring.app_id){
            const cids = details.cids
            const startdate = moment(details.startDayEpoch).format('YYYY-MM-DD');
            const enddate   = moment(details.endDayEpoch ).format('YYYY-MM-DD')
          

            const app_id  = params.qstring.app_id;
            
            const currentTime = common.getCurrentEpochTime();
            const fileName =  'CampaignExports_' + moment().format('MMMM Do YYYY,h:mm:ss a').toString().replace(" ","_");



            let q = buildCampaignExportsQuery(app_id, cids,startdate, enddate );

            logger.info(`======Campaign Exports Query created ==================> ${q}`)
     
            let jobs = {
                _id : common.db.ObjectID(),
                app_id: app_id,
                filename: fileName,
                deleted: false,
                status: "initiated",
                exportjobbaseurl: "",
                exportjobentityurl: "",
                processeddate: "",
                email: params.member.email,
                createdOn:currentTime,
                modifiedOn: currentTime,
                query: q
              };
              

              common.saveJobsData(jobs).then((jobsData) =>{
                logger.info(`Jobs data Saved=============>`)
                let payloadStatusTableName = "jobexportdatastatus_"+app_id;
                let jobId = uuid.v4();
            

                jobsData.query.mid = jobsData._id;
                
                jobsData.query.email = jobsData.email;
                

                const encodedQ = q.toString().replace(/'/ig, "''");
                let mainQuery = {
                    q :encodedQ,
                    email : jobsData.email,
                    serviceType :semusiConfig.ServiceName.CampaignReports,
                    mid : jobsData._id                         
                };

                let query = "INSERT INTO " + payloadStatusTableName + " (jobid,status) VALUES ('" + jobId + "','unprocessed');INSERT INTO exportdata_" + app_id + " (jobid,payload) VALUES ('" + jobId + "','" + JSON.stringify(mainQuery) + "');";


                // EXECUTE MAIN USER QUERY
                psqlUtils.runQuery(query)
                .then(s => {
                    
                    logger.info(`s==> ${s}`);
                    common.returnOutput(params,{});

                })
                .catch((err) => {
                    
                    logger.error(`error in Run Query ${err}`);

                    //Incase of Query failure update the status in the Jobs data

                    const job_status = 'Failed Due to Metadata'
                    common.updateJobsData(jobsData._id,job_status,app_id).then(()=>{
                        common.returnOutput(params,err);
                    }).catch(error =>{
                        logger.error(`ERROR IN THIS===${error}`)
                        common.returnOutput(params,error);
                    });

                });
            })
            .catch(error =>{
                logger.error(`catch error ::>>>${error}`);
            });

        }else{
            logger.error(`Error in Iinitiate Campaign Exports Appid Not Found`)
            common.returnOutput(params, {});
        } 
     }
    templateApi.getCampaignsByStatus = async function(params) {
        let match = {};
        if (params.qstring.status) {

            if (params.qstring.status === "ACTIVE") {
                match.st = {
                    $in: ["ACTIVE", "PENDING"]
                };
            } else {
                match.st = params.qstring.status;
            }
            if(params.qstring.cmpType && params.qstring.cmpType == "t"){
                match.cy = {
                    $in: ['t']
                }
            }else{
                match.cy = {
                    $in: ["g", "e",'u']
                };
            }

    }
    match['isdelete'] = false;
         //get app data
        const app = await common.retrieveAppSettings(params.qstring.app_id);

        //get default columns
        const DEFAULT_COLUMN = validate.getColumns("DEFAULT_COLUMN")
        
        //get column configuration if it is in app , else use default
        const columnStatus = (app.campaign_columns) ? app.campaign_columns : DEFAULT_COLUMN

        //  check if user is using search Filter 
        if(params.qstring["search[value]"] != '' && params.qstring["search[value]"] != undefined){
        
            let searchValue = params.qstring["search[value]"];
            match.$or = [
                { "cname": { $regex: searchValue, $options: 'i' } },  // Case-insensitive regex match
                { "segmentname": { $regex: searchValue, $options: 'i' } }  // Case-insensitive regex match
            ];
    
        }

        // get the page and length of the page for pagination
        const start = parseInt(params.qstring.start) || 0;  // DataTables start parameter
        const length = parseInt(params.qstring.length) || 10;  // DataTables length parameter
    
        common.db.collection('campaign_statsdata_' + params.qstring.app_id)
            .find(match)
            .sort({ sd: -1 })
            .skip(start)
            .limit(length)
            .toArray(function(err, campaigns) {
                if (err) {
                    logger.error(err);
                    common.returnOutput(params, { data: [], recordsTotal: 0, recordsFiltered: 0 });
                    return false;
                } else {
                    common.db.collection('campaign_statsdata_' + params.qstring.app_id).count(match, async function(err, totalRecords) {
                        if (err) {
                            logger.error(err);
                            common.returnOutput(params, { data: [], recordsTotal: 0, recordsFiltered: 0 });
                            return false;
                        }
                        let retObj = {
                            recordsTotal: totalRecords,
                            recordsFiltered: totalRecords,  // Adjust if you implement filtering
                            dynamicColumns: columnStatus,
                        }
                        // process the retrived campaigns
                       const camp_merged_data =  await updateCampaignsDaywiseStatus(params , campaigns, params.qstring.app_id, params.qstring.sd, params.qstring.ed )

                       formatCampaignDataForPagination(params, retObj, camp_merged_data)
                    });
                }
            });
    };

    templateApi.getPastAndActiveCampaigns = function(params) {
        let currentDate = common.getCurrentEpochTime() * 1000;
        common.db.collection('campaigns_' + params.qstring.app_id).find({
            "d": false,
            "sd": {
                $lte: currentDate
            },
            "t": "FEEDBACK",
            "st": "ACTIVE"
        }).sort({
            "ed": -1
        }).toArray(function(err, result) {
            if (!result) {
                result = {};
            }
            common.returnOutput(params, result);
        });
    };

    templateApi.getCampaignById = function(params) {
        if(params.qstring.cid.length == 24){
        common.db.collection('campaigns_' + params.qstring.app_id).findOne({
            "d": false,
            "_id": common.db.ObjectID(params.qstring.cid)
        }, function(err, result) {
            if (!result) {
                result = {};
                common.returnOutput(params, result);
            } else if (result) {
                if (result.t != 'Email') {
                    common.db.collection('templates_' + params.qstring.app_id).findOne({
                        "isTemplateDeleted": "false",
                        "_id": common.db.ObjectID(result.tid)
                    }, function(err, temp) {

                        result.data = temp;
                        let blob = semusiConfig.blobContainerCDN.slice(0, -1);
                        if(semusiConfig.uploadAssets[0]=="Azure"){
                            //incase of azure the blob is different and doesn't need slice it even needs append of appid
                            blob =  semusiConfig.blobContainerCDN + params.qstring.app_id 
                        }
                         temp.blobContainerCDN =  blob
                        common.returnOutput(params, result);
                    });
                } else {
                    common.returnOutput(params, result);
                }
            }

        });
    }else{
        result = {};
        common.returnOutput(params, result);   
    }
    };

    templateApi.getCampaignFeedbackById = function(params) {
        let argProps = {
                'cid': {
                    'required': true,
                    'type': 'String'
                },
                'sd': {
                    'required': true,
                    'type': 'Number'
                },
                'ed': {
                    'required': true,
                    'type': 'Number'
                },
                't': {
                    'required': true,
                    'type': 'String'
                }
            },
            feedbackProps = {};

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

        let campaignId = feedbackProps.cid;

        if (campaignId) {
            //check if this is a real active campaign in the system
            common.db.collection('campaigns_' + params.qstring.app_id).findOne({
                "_id": common.db.ObjectID(feedbackProps.cid)
            }, function(err, result) {

                if (!err) {
                    logger.info(result);

                    if (result) {
                        let _idObj = {};

                        switch (feedbackProps.t) {
                            case "m":
                                _idObj["oEntry"] = "$oMonth";
                                break;
                            case "d":
                                _idObj["oEntry"] = "$oDayOfMonth";
                                break;
                            case "y":
                                _idObj["oEntry"] = "$oYear";
                                break;
                            case "w":
                                _idObj["oEntry"] = "$oWeekOfYear";
                                break;
                        }
                        _idObj["value"] = "$rasValue";

                        let groupParam = {
                            $group: {}
                        };
                        groupParam.$group["_id"] = _idObj;
                        groupParam.$group["totalSum"] = {};
                        groupParam.$group["totalSum"]["$sum"] = 1;


                        common.db.collection('response_' + feedbackProps.cid).aggregate([{
                                $match: {
                                    "intime": {
                                        $gte: feedbackProps.sd,
                                        $lte: feedbackProps.ed
                                    }
                                }
                            },
                            {
                                $unwind: "$response"
                            },
                            {
                                $project: {
                                    "resQid": "$response.qid",
                                    "rasValue": "$response.value",
                                    "oDate": {
                                        "$add": [new Date(0), {
                                            "$multiply": ["$intime", 1000]
                                        }]
                                    },
                                    "oDayOfWeek": {
                                        "$dayOfWeek": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oMonth": {
                                        "$month": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oYear": {
                                        "$year": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oDayOfMonth": {
                                        "$dayOfMonth": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oWeekOfYear": {
                                        "$week": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oWeekOfYear": {
                                        "$week": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    }
                                }
                            },
                            groupParam,
                            {
                                $project: {
                                    value: "$_id.value",
                                    oEntry: "$_id.oEntry",
                                    totalSum: "$totalSum"
                                }
                            },
                            {
                                $sort: {
                                    totalSum: 1
                                }
                            }
                        ], function(err, result) {
                            if (!err) {
                                if (!result) {
                                    result = {};
                                }
                                logger.info(result);
                                common.returnOutput(params, result);
                            } else {
                                logger.error(err);
                            }
                        });
                    } else {
                        common.returnMessage(params, 404, 'Campaign not found or not active');
                        return false;
                    }
                } else {
                    common.returnMessage(params, 404, 'Campaign not found or not active');
                    return false;
                }
            });
        } else {
            common.returnMessage(params, 400, 'CampaignID not Present');
            return false;
        }
    };

    templateApi.getCampaignReachById = function(params) {

        let argProps = {
                'cid': {
                    'required': true,
                    'type': 'String'
                },
                'sd': {
                    'required': true,
                    'type': 'Number'
                },
                'ed': {
                    'required': true,
                    'type': 'Number'
                },
                't': {
                    'required': true,
                    'type': 'String'
                }
            },
            feedbackProps = {};

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

        let campaignId = feedbackProps.cid;

        if (campaignId) {
            //check if this is a real active campaign in the system
            common.db.collection('campaigns_' + params.qstring.app_id).findOne({
                "_id": common.db.ObjectID(feedbackProps.cid)
            }, function(err, result) {
                if (!err) {

                    if (result) {
                        let _idObj = {};

                        switch (feedbackProps.t) {
                            case "m":
                                _idObj["oEntry"] = "$oMonth";
                                break;
                            case "d":
                                _idObj["oEntry"] = "$oDayOfMonth";
                                break;
                            case "y":
                                _idObj["oEntry"] = "$oYear";
                                break;
                            case "w":
                                _idObj["oEntry"] = "$oWeekOfYear";
                                break;
                        }
                        _idObj["value"] = "$rasValue";

                        let groupParam = {
                            $group: {}
                        };
                        groupParam.$group["_id"] = _idObj;
                        groupParam.$group["totalSum"] = {};
                        groupParam.$group["totalSum"]["$sum"] = 1;


                        common.db.collection('response_' + feedbackProps.cid).aggregate([{
                                $match: {
                                    "intime": {
                                        $gte: feedbackProps.sd,
                                        $lte: feedbackProps.ed
                                    }
                                }
                            },
                            {
                                $unwind: "$response"
                            },
                            {
                                $match: {
                                    "response.qid": {
                                        $in: [100, 200, 300, 400]
                                    }
                                }
                            },
                            {
                                $project: {
                                    "resQid": "$response.qid",
                                    "rasValue": "$response.value",
                                    "oDate": {
                                        "$add": [new Date(0), {
                                            "$multiply": ["$intime", 1000]
                                        }]
                                    },
                                    "oDayOfWeek": {
                                        "$dayOfWeek": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oMonth": {
                                        "$month": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oYear": {
                                        "$year": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oDayOfMonth": {
                                        "$dayOfMonth": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oWeekOfYear": {
                                        "$week": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    },
                                    "oWeekOfYear": {
                                        "$week": {
                                            "$add": [new Date(0), {
                                                "$multiply": ["$intime", 1000]
                                            }]
                                        }
                                    }
                                }
                            },
                            groupParam,
                            {
                                $project: {
                                    value: "$_id.value",
                                    oEntry: "$_id.oEntry",
                                    totalSum: "$totalSum"
                                }
                            },
                            {
                                $sort: {
                                    totalSum: 1
                                }
                            }
                        ], function(err, result) {
                            if (!err) {
                                if (!result) {
                                    result = {};
                                }
                                common.returnOutput(params, result);
                            } else {
                                logger.error(err);
                            }
                        });
                    } else {
                        common.returnMessage(params, 404, 'Campaign not found or not active');
                        return false;
                    }
                } else {
                    common.returnMessage(params, 404, 'Campaign not found or not active');
                    return false;
                }
            });
        } else {
            common.returnMessage(params, 400, 'CampaignID not Present');
            return false;
        }
    };

    // * @param the campaign data and the appID 
    // getting the template data from tid in the campaign data , and saving both in cache  
    setCampaigDataAndTempDataAtCache = function(appId, campaign){

        let valueForRedis = campaign;
        let currentDate = parseInt(moment().valueOf() / 1000);
        campaign.ed = parseInt(campaign.ed/1000);

        // setting the name of the key 
        let key = "campaign_"+appId+"_"+campaign._id

        // calculating the seconds from current to end date 
        let difference = (campaign.ed - currentDate);
        common.db.collection('templates_' + appId).findOne({"isTemplateDeleted": "false", "_id": common.db.ObjectID(campaign.tid)}, function(err, template) {
            
            if (err) {
                    logger.error(err);
            } else {
                    valueForRedis.template_data = template;
                    cacheApi.setKey(key,JSON.stringify( valueForRedis));
                    cacheApi.expire(key, difference);
             }
        });
    }

     buildCampaignExportsQuery= function (app_id, cids, startdate, enddate) {
        let tableName = `events_${app_id}`;


        // Convert cids array to a string for the ANY clause
        let cidsString = cids.map(cid => `'${cid}'`).join(', ');        
        let eventsQuery = `SELECT did, segment->>'campid' as campaignid, dt FROM ${tableName} WHERE key ='Campaign_Clicked' AND segment->>'campid' = ANY(ARRAY[${cidsString}]) AND dt > '${startdate}'  AND dt < '${enddate}' order by dt ASC;`;

        
        return eventsQuery;
    }


    templateApi.initiateCopyProcess = function(params,copyCampaign,tid){

        common.db.collection('templates_' + params.qstring.app_id).findOne({'_id': common.db.ObjectID(tid)}, function(err, foundTemplate) {
            if (foundTemplate && !err) {
    
                let copyTemplate = foundTemplate;
                delete copyTemplate._id
                copyTemplate.template_name =  foundTemplate.template_name + ' copy ' + crypto.randomBytes(2).toString('hex');
                copyTemplate.createdOn = foundTemplate.createdOn
                copyTemplate.modifiedOn = ""
                copyTemplate.createdAt =  common.getCurrentEpochTime()
                copyTemplate.updatedAt = ""
                
                common.db.collection('templates_' + params.qstring.app_id).insert(copyTemplate, function(err, tmpl) {
                    if (!err && tmpl.ops) {
                        copyCampaign.tid = String(tmpl.ops[0]._id);

                        common.db.collection('campaigns_' + params.qstring.app_id).insert(copyCampaign, function(err, cmp) {
                            if(!err && cmp.ops) {
        
                                let copyId = cmp.ops[0]._id;
                                let copyName = cmp.ops[0].nm;
        
                               //Get Campaign from camp_stats collection as per cid _id(String)
        
                                common.db.collection('campaign_statsdata_' + params.qstring.app_id).findOne({_id: params.qstring.cid.toString()}, (err,existstats) => {
                                    if(!err && existstats) {
        
                                     //Make Copy of that campaign and insert in stats collection
                                        let copyStatsCamp = {};
                                        existstats.sd = parseInt(moment().valueOf());
                                        existstats.ed = parseInt(moment().add('days', 7).valueOf());
                                        existstats.st = "DRAFT";
                                        existstats._id = copyId.toString();
                                        existstats.cname = copyName
                                        existstats.pushedTo = 0;
                                        existstats.clickedTo = 0;
                                        existstats.ctr = 0;
                                        existstats.receivedTo = 0;
                                        existstats.conversion = 0;
                                        copyStatsCamp = existstats;
                                        copyStatsCamp['active'] = false;
                                        common.db.collection('campaign_statsdata_' + params.qstring.app_id).insert(copyStatsCamp, function(err, statscmp) {
            
                                            if (err) {
                                                //failed to insert Copied Campaigns in Campaign stats Collection
                                                logger.info("failed to insert Copied Campaigns in Campaign stats Collection err as ==>", err)
                                                common.returnOutput(params, err);
                                            }
                                            else if (!err && statscmp.ops) {
                                                //ALL GOOD SEND CAMP RESPONSE 
                                                common.returnOutput(params, copyStatsCamp);                         
                                            }
                                        });        
                                    }
                                    else{
                                      //failed to find Copied Campaigns in Campaign stats Collection
                                      logger.info("failed to find Copied Campaigns in Campaign stats Collection err as ==>", err)
                                        common.returnOutput(params, err);
                                    }
                                })
                            }
                            else{
                                //failed to insert Copied Campaigns in Campaigns Collection
                                logger.info("failed to insert Copied Campaigns in Campaigns Collection err as ==>", err)
                                common.returnOutput(params, err);
                            }
                        })
                    }
                });
            } else {
                logger.error("Error in finding template of Copied Campaigns err as ==>", err)
                common.returnOutput(params, err);
            }
        });
    }
    /**
     * populateAppInboxExpiry method return the expirytime
     * @param {*} appinbox_et 
     * @param {*} app_id 
     * @param {*} dlvy 
     * @returns 
     */

    templateApi.populateAppInboxExpiry = async function(appinbox_et, app_id, dlvy) {
        return new Promise((resolve, reject) => {
            if(appinbox_et ==  null || appinbox_et ==  undefined || appinbox_et ==  ""){
                common.db.collection('apps').find({'_id':common.db.ObjectID(app_id)},{"AppInboxExpiry":1}).toArray( function(err, data){
                    if(err ||!data){
                        logger.error("Error in Finding the app ");
                        reject(err)    
                      }
                        else{ 
                            if(data[0].AppInboxExpiry == undefined || data[0].AppInboxExpiry == null || data[0].AppInboxExpiry == ""){
                                resolve("");
                            }else{
                                resolve((dlvy === 'a' || dlvy === 'i' ) ?  moment().add(parseInt(data[0].AppInboxExpiry), 'd').valueOf() : "");
                            }
                        }
                    })
                     
                }else if(dlvy === 'd'){
                    resolve('')
                }
                else{
                    resolve(parseInt(appinbox_et))
                }  
        })
        
    }

    templateApi.createCampaign = function(params) {
        let argProps = {
                'nm': {
                    'required': true,
                    'type': 'String'
                },
                'sd': {
                    'required': false,
                    'type': 'Number'
                },
                'ed': {
                    'required': false,
                    'type': 'Number'
                },
                'cd': {
                    'required': false,
                    'type': 'Number'
                },
                'stag': {
                    'required': false,
                    'type': 'JSON'
                },
                'appinbox_et':{
                    'required':false,
                    'type': 'Number'
                },
                'ud': {
                    'required': false,
                    'type': 'Number'
                },
                'st': {
                    'required': false,
                    'type': 'String'
                },
                't': {
                    'required': false,
                    'type': 'String'
                },
                'd': {
                    'required': false,
                    'type': 'Boolean'
                },
                'tid': {
                    'required': false,
                    'type': 'String'
                },
                'pa': {
                    'required': false,
                    'type': 'Boolean'
                },
                'aud': {
                    'required': false,
                    'type': 'String'
                },
                'fre': {
                    'required': false,
                    'type': 'JSON'
                },
                'limitedTo': {
                    'required': false,
                    'type': 'String'
                },
                'limitVal': {
                    'required': false,
                    'type': 'String'
                },
                'liveEvent': {
                    'required': false,
                    'type': 'Boolean'
                },
                'daySelector': {
                    'required': false,
                    'type': 'String'
                },
                'days': {
                    'required': false,
                    'type': 'Array'
                },
                'timeSelector': {
                    'required': false,
                    'type': 'String'
                },
                'time': {
                    'required': false,
                    'type': 'Array'
                },
                'childIds': {
                    'required': false,
                    'type': 'Array'
                },
                'c_arr': {
                    'required': false,
                    'type': 'Array'
                },
                'm_sub': {
                    'required': false
                },
                'm_cont': {
                    'required': false
                },
                's_cfg': {
                    'required': false
                },
                'e_cfg': {
                    'required': false
                },
                'diff': {
                    'required': false
                },
                'delays': {
                    'required': false,
                    'type': 'Number'
                }, // delay state
                'delayi': {
                    'required': false,
                    'type': 'String'
                }, // input value
                'delayu': {
                    'required': false,
                    'type': 'String'
                }, // unit value
                'dlvy': {
                    'required': false,
                    'type': 'String'
                }, // unit value
                'cy': {
                    'required': false,
                    'type': 'String'
                }, // category value
                'AbTypeSelector' : {
                    'required': false,
                    'type': 'String'
                },
                'abTypeRange' : {
                    'required': false, 
                    'type': 'Array'
                },
                'email_cost':{
                    'required': false,
                    'type': 'Number'
                },
                'controlGroup' : {
                    'required' : false,
                    'type' : 'Number'
                }
            },



            newcampaign = {},
            result = {},
            defaultSegment = {
                all_aud: 'All Audience',
                all_android: 'All Android Users',
                all_ios: 'All IOS Users',
                all_web: 'All Web Users'
            };

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

        newcampaign.cd = moment().valueOf(); //Server gmt time.
        newcampaign.ud = moment().valueOf(); //server gmt time.
        newcampaign.variant = params.qstring.args.variant
        newcampaign.active = false; //server gmt time.
        newcampaign.c_maker = common.getMaskedEmail(params.member.email)
        newcampaign.time = { "t": newcampaign.time.map(Number) };
        common.db.collection('campaigns_' + params.qstring.app_id).findOne({
            "nm": newcampaign.nm
        }, function(err, existing) {
            if (!existing) {
                //Getting Reached Count From Timely Collection and Update Reachable Audience.

                //1.get audience name
                common.db.collection('audiencesegment_' + params.qstring.app_id).findOne({
                    "_id": common.db.ObjectID(params.qstring.args.aud),
                    "$or": [{
                        'isDeleted': false
                    }, {
                        'isDeleted': null
                    }]
                }, function(err, audience) {
                    if (err) {
                        logger.error(err);
                        common.returnMessage(params, 500, 'Error in getting audience segment');
                    } else {
                        if (audience) {
                            let aud_name = audience.name;
                            let aud_reach = audience.range;
                            let segmentInfo = JSON.stringify(audience.segmentinfo);

                            //Getting data from timely sessions

                            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) {
                                if (!err && data && data.length) {
                                    let timely_data = data[0];
                                    if (aud_name == defaultSegment.all_aud) {
                                        aud_reach = (timely_data.android_active_users ? parseInt(timely_data.android_active_users) : 0) + (timely_data.ios_active_users ? parseInt(timely_data.ios_active_users) : 0) + (timely_data.web_active_users ? parseInt(timely_data.web_active_users) : 0);
                                    }
                                    if (aud_name == defaultSegment.all_android) {
                                        aud_reach = timely_data.android_active_users ? timely_data.android_active_users : 0;
                                    }
                                    if (aud_name == defaultSegment.all_ios) {
                                        aud_reach = timely_data.ios_active_users ? timely_data.ios_active_users : 0;
                                    }
                                    if (aud_name == defaultSegment.all_web) {
                                        aud_reach = timely_data.web_active_users ? timely_data.web_active_users : 0;
                                    }

                                }
                                //Updating Reach Count in Audience Collection
                                    common.db.collection('audiencesegment_' + params.qstring.app_id).update({
                                        "_id": common.db.ObjectID(params.qstring.args.aud),

                                    }, {
                                        $set: {
                                            range: aud_reach
                                        }
                                    }, function(err, updatedaud) {
                                        if (updatedaud && !err) {

                                            ///////////////////////
                                            //Inserting Data
                                            newcampaign['createdAt'] = common.getCurrentEpochTime();
                                             // parsing the string into Int 
                                            newcampaign.sd= parseInt(newcampaign.sd)
                                            newcampaign.ed= parseInt(newcampaign.ed)
                                            newcampaign.ud= parseInt(newcampaign.ud)
                                            if(newcampaign.appinbox_et ==  null || newcampaign.appinbox_et ==  undefined || newcampaign.appinbox_et ==  ""){
                                                templateApi.populateAppInboxExpiry(newcampaign.appinbox_et, params.qstring.app_id, params.qstring.args.dlvy).then((data) => {
                                                    if(data){
                                                        newcampaign.appinbox_et = data;
                                                      }else{
                                                        newcampaign.appinbox_et = "";
                                                      }
                                                        common.db.collection('campaigns_' + params.qstring.app_id).insert(newcampaign, function(err, cmp) {
                                                            if (!err && cmp.ops) {
                                                                // saving campaign data and templates in cache
                                                                setCampaigDataAndTempDataAtCache(params.qstring.app_id, newcampaign);
                                                                logger.info("campaign inserted in campaigns collection", cmp)
                                                                newcampaign._id = cmp.ops[0]._id;
                                                            }
                                                            if(newcampaign.abTypeRange && newcampaign.abTypeRange.length>0){
                                                                newcampaign.abTypeRange.forEach((variant) => {
                                                                    variant.pushedTo = 0;
                                                                    variant.receivedTo = 0;
                                                                    variant.ctr = 0;
                                                                    variant.clickedTo = 0;
                                                                    variant.conversion = 0;
                                                                }) 
                                                            }  
        
                                                            //Inserting data in camp_stats collection
                                                            let newCamp = {
                                                                "_id": newcampaign._id.toString(),
                                                                "pushedTo": 0,
                                                                "ctr": 0,
                                                                "clickedTo": 0,
                                                                "conversion": 0,
                                                                "st": params.qstring.args.st,
                                                                "cname": xss(params.qstring.args.nm),
                                                                "type": params.qstring.args.t,
                                                                "sd": parseInt(params.qstring.args.sd),
                                                                "ed": parseInt(params.qstring.args.ed),
                                                                "aud": params.qstring.args.aud,
                                                                "isdelete": false,
                                                                "active": false,
                                                                // "reach": aud_reach,
                                                                "reach": 0,
                                                                "segmentname": aud_name,
                                                                "segmentinfo": segmentInfo,
                                                                "variant":params.qstring.args.variant,
                                                                "cy":newcampaign.cy,
                                                                "c_maker":newcampaign.c_maker,
                                                                "dlvy":newcampaign.dlvy,
                                                                "variance" : (newcampaign.abTypeRange && newcampaign.abTypeRange.length > 0) ? newcampaign.abTypeRange : []
                                                            }
                                                            // Check Campaign Data Existance
                                                            common.db.collection('campaign_statsdata_' + params.qstring.app_id).findOne({
                                                                "nm": newCamp.cname
                                                            }, function(err, existing) {
                                                                if (!existing) {
                                                                    common.db.collection('campaign_statsdata_' + params.qstring.app_id).insert(newCamp, {
                                                                        forceServerObjectId: false
                                                                    }, function(err, cmp) {
                                                                        if (!err && cmp.ops) {
                                                                            logger.info("campaign inserted in campaign_statsdata collection", cmp)
                                                                            common.returnOutput(params, newcampaign);
        
                                                                        } else {
                                                                            common.returnOutput(params, "Error in inserting camp_stats data");
                                                                        }
        
                                                                    });
                                                                } else {
                                                                    common.returnOutput(params, "Campaign already exist");
                                                                }
        
        
                                                            });
        
        
                                                        });
                                                    })
                                            }else{
                                                common.db.collection('campaigns_' + params.qstring.app_id).insert(newcampaign, function(err, cmp) {
                                                    if (!err && cmp.ops) {
                                                        // saving campaign data and templates in cache
                                                        setCampaigDataAndTempDataAtCache(params.qstring.app_id, newcampaign);
                                                        logger.info("campaign inserted in campaigns collection", cmp)
                                                        newcampaign._id = cmp.ops[0]._id;
                                                    }
                                                    if(newcampaign.abTypeRange && newcampaign.abTypeRange.length>0){
                                                        newcampaign.abTypeRange.forEach((variant) => {
                                                            variant.pushedTo = 0;
                                                            variant.receivedTo = 0;
                                                            variant.ctr = 0;
                                                            variant.clickedTo = 0;
                                                            variant.conversion = 0;
                                                        }) 
                                                    }  

                                                    //Inserting data in camp_stats collection
                                                    let newCamp = {
                                                        "_id": newcampaign._id.toString(),
                                                        "pushedTo": 0,
                                                        "ctr": 0,
                                                        "clickedTo": 0,
                                                        "conversion": 0,
                                                        "st": params.qstring.args.st,
                                                        "cname": xss(params.qstring.args.nm),
                                                        "type": params.qstring.args.t,
                                                        "sd": parseInt(params.qstring.args.sd),
                                                        "ed": parseInt(params.qstring.args.ed),
                                                        "aud": params.qstring.args.aud,
                                                        "isdelete": false,
                                                        "active": false,
                                                         // "reach": aud_reach,
                                                                "reach": 0,
                                                        "segmentname": aud_name,
                                                        "segmentinfo": segmentInfo,
                                                        "variant":params.qstring.args.variant,
                                                        "cy":newcampaign.cy,
                                                        "c_maker":newcampaign.c_maker,
                                                        "dlvy":newcampaign.dlvy,
                                                        "variance" : (newcampaign.abTypeRange && newcampaign.abTypeRange.length > 0) ? newcampaign.abTypeRange : []
                                                    }
                                                    // Check Campaign Data Existance
                                                    common.db.collection('campaign_statsdata_' + params.qstring.app_id).findOne({
                                                        "nm": newCamp.cname
                                                    }, function(err, existing) {
                                                        if (!existing) {
                                                            common.db.collection('campaign_statsdata_' + params.qstring.app_id).insert(newCamp, {
                                                                forceServerObjectId: false
                                                            }, function(err, cmp) {
                                                                if (!err && cmp.ops) {
                                                                    logger.info("campaign inserted in campaign_statsdata collection", cmp)
                                                                    common.returnOutput(params, newcampaign);

                                                                } else {
                                                                    common.returnOutput(params, "Error in inserting camp_stats data");
                                                                }

                                                            });
                                                        } else {
                                                            common.returnOutput(params, "Campaign already exist");
                                                        }


                                                    });


                                                });
                                            }

                                        } else {
                                            common.returnMessage(params, 500, 'Error in updating reach count in audience');
                                        }
                                    })

                            });


                        }
                    }
                })


            } else {
                result.status = "error";
                common.returnOutput(params, result);
            }

        });
    };

     /**
     * validateTemplateName method return the true or false if template name is found or not
     * @param {*} template_name  template name 
     * @returns Boolean value o false or true
     */

    templateApi.validateTemplateName = function (params) {
    
        if (params.qstring.template_name) {

            let name = new RegExp(["^", params.qstring.template_name, "$"].join(""), "i");
            common.db.collection('templates_' + params.qstring.app_id).find({ "template_name": name,  "template_type": params.qstring.type, "$or": [{ 'isTemplateDeleted': { '$eq': "false" } }] }).toArray(function (err, result) {
                if (err) {
                    logger.error("Error in validating Template name", err);
                    common.returnOutput(params, 500, "Error");
                } else {
                    logger.info(`Validating Template  Name ==> ${name} Result Length ${result.length} `)
                    common.returnOutput(params, (result.length == 0) ? false : true);
                }
            });

        }
        else {
            common.returnOutput(params, 500, "Error While Validating Template name");
        }
    }

    templateApi.updateCampaign = function(params) {
        let adminAccess = (params.member.global_admin) ? true : (params.member.user_role.admin) ? params.member.user_role.admin : []
        let managerAccess = (params.member.global_admin) ? true : (params.member.user_role.manager) ? params.member.user_role.manager : []
        let argProps = {
                'nm': {
                    'required': true,
                    'type': 'String'
                },
                'sd': {
                    'required': false,
                    'type': 'Number'
                },
                'ed': {
                    'required': false,
                    'type': 'Number'
                },
                'ud': {
                    'required': false,
                    'type': 'Number'
                },
                'appinbox_et': {
                    'required': false,
                    'type': 'Number'
                },
                'stag': {
                    'required': false,
                    'type': 'JSON'
                },
                't': {
                    'required': false,
                    'type': 'String'
                },
                'd': {
                    'required': false,
                    'type': 'Boolean'
                },
                'tid': {
                    'required': false,
                    'type': 'String'
                },
                'aud': {
                    'required': false,
                    'type': 'String'
                },
                'fre': {
                    'required': false,
                    'type': 'JSON'
                },
                'limitedTo': {
                    'required': false,
                    'type': 'String'
                },
                'limitVal': {
                    'required': false,
                    'type': 'String'
                },
                'liveEvent': {
                    'required': false,
                    'type': 'Boolean'
                },
                'daySelector': {
                    'required': false,
                    'type': 'String'
                },
                'days': {
                    'required': false,
                    'type': 'Array'
                },
                'timeSelector': {
                    'required': false,
                    'type': 'String'
                },
                'time': {
                    'required': false,
                    'type': 'Array'
                },
                'childIds': {
                    'required': false,
                    'type': 'Array'
                },
                'c_arr': {
                    'required': false,
                    'type': 'Array'
                },
                'm_sub': {
                    'required': false
                },
                'm_cont': {
                    'required': false
                },
                's_cfg': {
                    'required': false
                },
                'e_cfg': {
                    'required': false
                },
                'diff': {
                    'required': false
                },
                'delays': {
                    'required': false,
                    'type': 'Number'
                }, // delay state
                'delayi': {
                    'required': false,
                    'type': 'String'
                }, // input value
                'delayu': {
                    'required': false,
                    'type': 'String'
                }, // unit value
                'dlvy': {
                    'required': false,
                    'type': 'String'
                }, // unit value
                'cy': {
                    'required': false,
                    'type': 'String'
                }, // category value
                'AbTypeSelector' : {
                    'required': false,
                    'type': 'String'
                },
                'abTypeRange' : {
                    'required': false, 
                    'type': 'Array'
                },
                'email_cost':{
                    'required': false,
                    'type': 'Number'
                },
                'controlGroup' : {
                    'required' : false,
                    'type' : 'Number'
                }
            },
            campaign = {},
            defaultSegment = {
                all_aud: 'All Audience',
                all_android: 'All Android Users',
                all_ios: 'All IOS Users',
                all_web: 'All Web Users'
            },
            result = {};
        if (!(campaign = common.validateArgs(params.qstring.args, argProps))) {
            common.returnMessage(params, 422, 'Validation error');
            return false;
        }

        campaign.ud = moment().valueOf(); //Updated time should be the server time.
        // set variant state in case of edit 
        campaign.variant = params.qstring.args.variant
        campaign.c_maker = common.getMaskedEmail(params.member.email)
        campaign.time = { "t": campaign.time.map(Number) };

        common.db.collection('campaigns_' + params.qstring.app_id).findOne({
            '_id': common.db.ObjectID(params.qstring.cid)



        }, function(err, existing) {
            if (existing.st === 'ACTIVE'){
                if (!params.member.global_admin && (Array.isArray(adminAccess) && !adminAccess.includes(params.qstring.app_id) && Array.isArray(managerAccess) && !managerAccess.includes(params.qstring.app_id) )) {
                    common.returnMessage(params, 500, ErrorMsg.getErrorMessage("UnAuthorizedUser"));
                } 
            }
            /////////////////////////

            //Getting Reached Count From Timely Collection and Update Reachable Audience.

            //1.get audience name
            common.db.collection('audiencesegment_' + params.qstring.app_id).findOne({
                "_id": common.db.ObjectID(params.qstring.args.aud),
                "$or": [{
                    'isDeleted': false
                }, {
                    'isDeleted': null
                }]
            }, function(err, audience) {
                if (err || audience == null) {
                    common.returnMessage(params, 500, 'Error in getting audience segmwent');
                } else {
                    if (audience) {
                        let aud_name = audience.name;
                        let aud_reach = audience.range;
                        let segmentInfo = JSON.stringify(audience.segmentinfo);

                        //Getting data from timely sessions

                        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) {
                            if (!err && data) {

                                let timely_data = data[0];

                                if (aud_name == defaultSegment.all_aud) {
                                    aud_reach = (timely_data.android_active_users ? parseInt(timely_data.android_active_users) : 0) + (timely_data.ios_active_users ? parseInt(timely_data.ios_active_users) : 0) + (timely_data.web_active_users ? parseInt(timely_data.web_active_users) : 0);
                                }
                                if (aud_name == defaultSegment.all_android) {
                                    aud_reach = timely_data.android_active_users ? timely_data.android_active_users : 0;
                                }
                                if (aud_name == defaultSegment.all_ios) {
                                    aud_reach = timely_data.ios_active_users ? timely_data.ios_active_users : 0;
                                }
                                if (aud_name == defaultSegment.all_web) {
                                    aud_reach = timely_data.web_active_users ? timely_data.web_active_users : 0;
                                }
                                //Updating Reach Count in Audience Collection

                                common.db.collection('audiencesegment_' + params.qstring.app_id).update({
                                    "_id": common.db.ObjectID(params.qstring.args.aud),

                                }, {
                                    $set: {
                                        range: aud_reach
                                    }
                                }, function(err, updatedaud) {
                                    if (updatedaud && !err) {

                                        /////////////////////////////
                                        //Updating Campaign Data

                                         // parsing the string into Int 
                                        campaign.sd= parseInt(campaign.sd)
                                        campaign.ed= parseInt(campaign.ed)
                                        campaign.ud= parseInt(campaign.ud)
                                        templateApi.populateAppInboxExpiry(campaign.appinbox_et, params.qstring.app_id, params.qstring.args.dlvy).then((data) => {
                                            if(data){
                                                campaign.appinbox_et = data;
                                            } else{
                                                campaign.appinbox_et = "";
                                            }
                                        if (existing && existing._id == params.qstring.cid) {
                                            campaign['updatedAt'] = common.getCurrentEpochTime();
                                            common.db.collection('campaigns_' + params.qstring.app_id).update({
                                                '_id': common.db.ObjectID(params.qstring.cid)
                                            }, {
                                                $set: campaign
                                            }, {
                                                safe: true
                                            }, function(err, isOk) {
                                                logger.info(JSON.stringify(isOk))
                                                if (!err) {
                                                    campaign._id = existing._id;
                                                    // saving updated campaign data and templates in cache
                                                    setCampaigDataAndTempDataAtCache(params.qstring.app_id, campaign);
                                                    logger.info("campaign updated in campaigns collection", isOk)
                                                    campaign._id = params.qstring.cid;
                                                    if(campaign.abTypeRange && campaign.abTypeRange.length>0){
                                                        campaign.abTypeRange.forEach((variant) => {
                                                            variant.pushedTo = 0;
                                                            variant.receivedTo = 0;
                                                            variant.ctr = 0;
                                                            variant.clickedTo = 0;
                                                            variant.conversion = 0;
                                                            
                                                        })
                                                       }


                                                    //Updating data in camp_stats collection
                                                    let newCamp = {
                                                        "_id": params.qstring.cid.toString(),
                                                        "pushedTo": 0,
                                                        "ctr": 0,
                                                        "clickedTo": 0,
                                                        "conversion": 0,
                                                        "cname": xss(params.qstring.args.nm),
                                                        "type": params.qstring.args.t,
                                                        "sd": parseInt(params.qstring.args.sd),
                                                        "ed": parseInt(params.qstring.args.ed),
                                                        "aud": params.qstring.args.aud,
                                                        "cy": params.qstring.args.cy,
                                                        "isdelete": false,
                                                         // "reach": aud_reach,
                                                                "reach": 0,
                                                        "segmentname": aud_name,
                                                        "segmentinfo": segmentInfo,
                                                        "c_maker": common.getMaskedEmail(params.member.email),
                                                        "dlvy":campaign.dlvy,
                                                        "variance" : (campaign.abTypeRange && campaign.abTypeRange.length > 0) ? campaign.abTypeRange : []
                                                    }
                                                    common.db.collection('campaign_statsdata_' + params.qstring.app_id).findOne({
                                                        "cname": newCamp.cname
                                                    }, function(err, datasetresult) {
                                                        if(datasetresult != null){
                                                            newCamp.pushedTo = parseInt(datasetresult.pushedTo)
                                                            newCamp.ctr = parseInt(datasetresult.ctr)
                                                            newCamp.clickedTo = parseInt(datasetresult.clickedTo)
                                                            newCamp.conversion = parseInt(datasetresult.conversion)
                                                            
                                                        }
                                                        if (datasetresult && datasetresult._id == newCamp._id) {

                                                            common.db.collection('campaign_statsdata_' + params.qstring.app_id).update({
                                                                '_id': params.qstring.cid.toString()
                                                            }, {
                                                                $set: newCamp
                                                            }, {
                                                                safe: true
                                                            }, function(err, isOk) {
                                                                if (err) {
                                                                    logger.error(err)
                                                                    common.returnOutput(params, "Error in update campaign stats");
                                                                }
                                                                logger.info("Campaign updated in campaign_statsdata_ collection", isOk);
                                                                common.returnOutput(params, campaign);
                                                            });
                                                        } else if (datasetresult && datasetresult._id != params.qstring.cid) {
                                                            common.returnOutput(params, "Error");
                                                        } else {
                                                            common.db.collection('campaign_statsdata_' + params.qstring.app_id).update({
                                                                '_id': newCamp._id.toString()
                                                            }, {
                                                                $set: newCamp
                                                            }, {
                                                                safe: true
                                                            }, function(err, isOk) {
                                                                if (err) {
                                                                    common.returnOutput(params, "Error in update campaign stats");
                                                                }
                                                                common.returnOutput(params, campaign);
                                                            });
                                                        }
                                                    });

                                                } else {
                                                    common.returnOutput(params, err);
                                                }
                                            });
                                        } else if (existing && existing._id != params.qstring.cid) {
                                            result.status = "error";
                                            common.returnOutput(params, result);
                                        } else {
                                            campaign['updatedAt'] = common.getCurrentEpochTime();
                                            common.db.collection('campaigns_' + params.qstring.app_id).update({
                                                '_id': common.db.ObjectID(params.qstring.cid)
                                            }, {
                                                $set: campaign
                                            }, {
                                                safe: true
                                            }, function(err, isOk) {
                                                campaign._id = params.qstring.cid;

                                                //Updating data in camp stats collection

                                                let newCamp = {
                                                    "_id": params.qstring.cid.toString(),
                                                    "pushedTo": 0,
                                                    "ctr": 0,
                                                    "clickedTo": 0,
                                                    "conversion": 0,
                                                    "cname": xss(params.qstring.args.nm),
                                                    "type": params.qstring.args.t,
                                                    "sd": parseInt(params.qstring.args.sd),
                                                    "ed": parseInt(params.qstring.args.ed),
                                                    "aud": params.qstring.args.aud,
                                                    "isdelete": false,
                                                     // "reach": aud_reach,
                                                                "reach": 0,
                                                    "segmentname": aud_name,
                                                    "segmentinfo": segmentInfo,
                                                    "dlvy":campaign.dlvy,
                                                    "variance" : (campaign.abTypeRange && campaign.abTypeRange.length > 0) ? campaign.abTypeRange : []
                                                }
                                                common.db.collection('campaign_statsdata_' + params.qstring.app_id).findOne({
                                                    "cname": newCamp.cname
                                                }, function(err, datasetresult) {

                                                    if(datasetresult != null){
                                                        newCamp.pushedTo = parseInt(datasetresult.pushedTo)
                                                        newCamp.ctr = parseInt(datasetresult.ctr)
                                                        newCamp.clickedTo = parseInt(datasetresult.clickedTo)
                                                        newCamp.conversion = parseInt(datasetresult.conversion)
                                                        
                                                    }
                                                    if (datasetresult && datasetresult._id == newCamp._id) {

                                                        common.db.collection('campaign_statsdata_' + params.qstring.app_id).update({
                                                            '_id': params.qstring.cid.toString()
                                                        }, {
                                                            $set: newCamp
                                                        }, {
                                                            safe: true
                                                        }, function(err, isOk) {

                                                            if (err) {
                                                                common.returnOutput(params, "Error in update campaign stats");
                                                            }
                                                            common.returnOutput(params, campaign);


                                                        });
                                                    } else if (datasetresult && datasetresult._id != params.qstring.cid) {
                                                        common.returnOutput(params, "Error");
                                                    } else {
                                                        common.db.collection('campaign_statsdata_' + params.qstring.app_id).update({
                                                            '_id': newCamp._id.toString()
                                                        }, {
                                                            $set: newCamp
                                                        }, {
                                                            safe: true
                                                        }, function(err, isOk) {
                                                            if (err) {
                                                                common.returnOutput(params, "Error in update campaign stats");
                                                            }
                                                            common.returnOutput(params, campaign);
                                                        });
                                                    }
                                                });


                                            });
                                        }
                                     })
                                    } else {
                                        common.returnMessage(params, 500, 'Error in updating reach count in audience');
                                    }
                                })

                            } else {
                                common.returnMessage(params, 500, 'Error in getting timely session data');
                            }

                        });


                    }
                }
            })

        });

    };




    templateApi.copyCampaign = function(params) {
        let argProps = {
            'ud': {
                'required': false,
                'type': 'Number'
            },
            'd': {
                'required': false,
                'type': 'Boolean'
            },
        }
        campaign = {}, result = {};
        if (!(campaign = common.validateArgs(params.qstring.args, argProps))) {
            common.returnMessage(params, 400, 'Not enough args');
        }
        //Get Campaign from campaignCollection as per cid. _id(ObjectId);
        common.db.collection('campaigns_' + params.qstring.app_id).findOne({_id: common.db.ObjectID(params.qstring.cid), d: false}, (err,existing) => {
          
            if(!err && existing) {                 
               //Make Copy of that campaign and insert in collection
                let copyCampaign = {};
                existing.sd = moment().valueOf();
                existing.ed = moment().add('days', 7).valueOf();
                existing.st = "DRAFT";
                existing._id = common.db.ObjectID();
                existing.nm = existing.nm + ' copy ' + crypto.randomBytes(2).toString('hex');
                copyCampaign = existing;
                copyCampaign['active'] = false;
                


                // Setting Default of 7 days 
                const defaultExpiryDays = 7;


                common.db.collection('apps').find({ '_id': common.db.ObjectID(params.qstring.app_id) }).toArray(function (err, data) {
                    if (err || !data) {
                        logger.error("Error in Finding the app ");
                    }
                    else {
                        //get App Details 
                        const app = data[0]

                        // if App has the Appinbox expiry set 
                        if(app.AppInboxExpiry){

                            // will set appInbox  
                            const days =  parseInt(app.AppInboxExpiry)
                            copyCampaign.appinbox_et = moment().add(days, 'days').valueOf();
                            logger.info(`Set appinbox Expiriy from Apps`)
                       

                

                            logger.info(`initiating copy template Proceess`)
                            //initate Copy Process
                            templateApi.initiateCopyProcess(params, copyCampaign, existing.tid)

                        }else{
                            //incase apps collection don't have the appinbox expiriy days
   
                            // getting days from global  settings 
                            common.getGlobalSettings()
                            .then(globalSetting => {
          
                                // if global setting exists and has expiry days, then set otherwise set default of 7 Days 
                                const days =  (globalSetting.appinbox && globalSetting.appinbox.expriry_t) ? parseInt(globalSetting.appinbox.expriry_t) : defaultExpiryDays
                                copyCampaign.appinbox_et = moment().add(days, 'days').valueOf();
                                logger.info(`Set appinbox Expiriy from Global settings`)

                                // code moves to finally
                       
                            })
                            .catch(error => {

                                logger.info(`Set appinbox Expiriy Of default days `)
                                //if any error during getting global settings  use default of 7 days 
                                copyCampaign.appinbox_et = moment().add(defaultExpiryDays, 'days').valueOf();

                                // code moves to finally

                            }).finally(() => {
                                 
                                logger.info(`initiating copy template Proceess`)

                //initate Copy Process
                templateApi.initiateCopyProcess(params,copyCampaign,existing.tid)
                            });

                        }
                    }
                })
            }
            else{
                common.returnOutput(params, err);
            }
        })        
        
    }

    // set campaign what into redis
    templateApi.setCampaignEventWhat = function(appid, campid, audId) {
        common.db.collection('audiencesegment_' + appid).findOne({
            "_id": common.db.ObjectID(audId)
        }, {
            segmentinfo: 1
        }, function(err, audiencesegments) {
            if (!err && audiencesegments) {
                if (audiencesegments.segmentinfo.what.length > 0) {
                    let steps = [];
                    let eventLists = [];
                    audiencesegments.segmentinfo.what.forEach(function(item) {
                        let obj = {
                            "event_collection": 'event_' + appid + "_" + item.operand,
                            "with_actors": true,
                            "actor_property": 'did',
                            "timeframe": "previous_90_days",
                            filters: []
                        };
                        if (!item.value) {
                            obj.inverted = true;
                        }
                        steps.push(obj);
                        eventLists.push(item.operand);
                    });
                }
            }
        });
    }


    //This API will update the campaign state for childAppIds.
    templateApi.updateCampaignState = function(params) {
        let argProps = {
                'campid': {
                    'required': true,
                    'type': 'String'
                },
                'childAppId': {
                    'required': false,
                    'type': 'String'
                },
                'approved': {
                    'required': false,
                    'type': 'Boolean'
                }
            },
            args = {},
            result = {};
        if (!(args = common.validateArgs(params.postData.args, argProps))) {
            common.returnMessage(params, 400, 'Not enough args');
            return false;
        }
        common.db.collection('campaigns_' + params.postData.app_id).findOne({
            _id: common.db.ObjectID(args.campid),
            "childIds.id": args.childAppId
        }, function(err, cmp) {
            if (cmp) {
                let updatedAt = common.getCurrentEpochTime();
                common.db.collection('campaigns_' + params.postData.app_id).update({
                    _id: common.db.ObjectID(args.campid),
                    "childIds.id": args.childAppId
                }, {
                    $set: {
                        "childIds.$.approved": args.approved,
                        ud: moment().valueOf(),
                        'updatedAt': updatedAt
                    }
                }, function(err, result) {
                    if (err) {
                        common.returnMessage(params, 400, 'Failed');
                    } else {
                        common.returnMessage(params, 200, 'Success');
                        //If this campaign is active send push to its users.
                        if (cmp && cmp.st == "ACTIVE") {
                            let pushCommand = "#CAMPAIGNUPDATE";
                            if (cmp.segmentname == semusiConfig.atRiskAudience) {
                                pushCommand = "#ATRISK#" + params.qstring.cid;
                            }
                            params.qstring.app_id = params.postData.app_id;
                            params.childAppIds = [params.postData.args.childAppId];
                            templateApi.silentPushForCampaignUpdate(params, cmp, pushCommand, args.approved);
                        }
                    }
                });
            } else {
                common.returnMessage(params, 400, 'There is no campaign with id as:' + args.campid);
            }

        });
    };

    templateApi.getTemplatesById = function(params) {
        let appDetail = {};
        //mark this device as uninstalled
        common.db.collection('app_users' + params.qstring.app_id).findOne({
            'did': params.qstring.did
        }, function(err, delDevice) {
            if (delDevice && delDevice.history && delDevice.history.length > 0) {
                let historyCount = delDevice.history.length;
                let refObj = {};
                // put utm campaign name if exists into install
                if (delDevice.history[historyCount - 1].utm_campaign) {
                    refObj.utm_campaign = delDevice.history[historyCount - 1].utm_campaign;
                }
                // define other required property
                refObj.refname = delDevice.history[historyCount - 1].refname;
                refObj.type = "U";
                refObj.unBy = "feedback";
                refObj.dtEntry = common.getCurrentEpochTime();
                let updatedAt = refObj.dtEntry;
                if (delDevice.tz) {
                    refObj.dtEntry = parseInt(refObj.dtEntry) + parseInt(delDevice.tz);
                }

                refObj.isnew = true;
                //Check if this is not the first time uninstall.
                delDevice.history.forEach(function(h) {
                    if (h.type == "U") {
                        refObj.isnew = false;
                    }
                });

                common.db.collection('app_users' + params.qstring.app_id).findOne({
                    'did': params.qstring.did,
                    "history.type": "I"
                }, {
                    "history": {
                        "$slice": -1
                    },
                    "p": 1,
                    "av": 1,
                    "cc": 1,
                    "cty": 1,
                    "pv": 1,
                    "_id": 1,
                    "did": 1,
                    "le": 1,
                    "user_info": 1,
                    "gender": 1
                }, function(err, deviceToBeDeleted) {
                    if (deviceToBeDeleted) {
                        if (deviceToBeDeleted.history.length >= 1) {
                            if (deviceToBeDeleted.history[0].type == "I") {
                                common.db.collection('app_users' + params.qstring.app_id).update({
                                    'did': params.qstring.did
                                }, {
                                    '$set': {
                                        'active': false,
                                        'updatedAt': updatedAt
                                    },
                                    '$addToSet': {
                                        "history": refObj
                                    }
                                }, function(err, responseData) {
                                    if (err) {
                                        logger.error('err:' + JSON.stringify(err))
                                    } else {
                                        let gender = (deviceToBeDeleted.gender) ? deviceToBeDeleted.gender : 'unknown';
                                        common.manageActiveUser(params.qstring.app_id, deviceToBeDeleted.p.toLowerCase(), false); // manage active users into optimise collection
                                        common.usersActiveManage(params.qstring.app_id, deviceToBeDeleted.p, gender, -1);

                                        // send email when uninstall comes
                                        if (delDevice.user_info) {
                                            common.db.collection('apps').findOne({
                                                "_id": common.db.ObjectID(params.qstring.app_id),
                                                "isAppDeleted": "false"
                                            }, function(err, appData) {
                                                if (!err && appData && appData.emailCfg) {
                                                    commonEvent.sendEmailWithNetcore(delDevice.user_info.e, "This is test mail", "uninstall mail", appData.emailCfg['netcore']);
                                                }
                                            });
                                        }
                                        // uninstalls script
                                        let history = deviceToBeDeleted.history[0];
                                        let uninstallDate = moment(refObj.dtEntry * 1000).format('YYYY-MM-DD');
                                        let installDate = moment(history.dtEntry * 1000).format('YYYY-MM-DD');

                                        let diff = moment(uninstallDate).diff(moment(installDate), 'days', true);
                                        diff = parseInt(Math.abs(diff));
                                        diff = (diff == 0) ? 'o' : diff;
                                        let dashboardObj = {};
                                        deviceToBeDeleted.pv = deviceToBeDeleted.pv.replace(/\./g, "_");
                                        deviceToBeDeleted.av = deviceToBeDeleted.av.replace(/\./g, "_");
                                        history.refname = history.refname.replace(/\./g, "-");
                                        deviceToBeDeleted.cty = deviceToBeDeleted.cty.replace(/\./g, "-");
                                        deviceToBeDeleted.cc = deviceToBeDeleted.cc.replace(/\./g, "-");

                                        refObj.dtEntry = refObj.dtEntry * 1000;
                                        let id = common.timelyEventId(params.qstring.app_id, refObj.dtEntry);

                                        common.fillHistoryObjectWithEpoch(refObj.dtEntry, dashboardObj, deviceToBeDeleted.p.toLowerCase(), deviceToBeDeleted.pv + "." + deviceToBeDeleted.av + "." + history.refname + "." + deviceToBeDeleted.cc + "." + deviceToBeDeleted.cty + "." + diff);
                                        common.fillHistoryObjectWithEpoch(refObj.dtEntry, dashboardObj, deviceToBeDeleted.p.toLowerCase(), "totalUn", 1, false);
                                        // manage cohort
                                        dashboardObj[deviceToBeDeleted.p.toLowerCase() + "." + moment(refObj.dtEntry).format("YYYY_M_D") + ".cohort." + diff] = 1;

                                        if (refObj.isnew) {
                                            common.fillHistoryObjectWithEpoch(refObj.dtEntry, dashboardObj, deviceToBeDeleted.p.toLowerCase(), deviceToBeDeleted.pv + "." + deviceToBeDeleted.av + "." + history.refname + "." + deviceToBeDeleted.cc + "." + deviceToBeDeleted.cty + ".un");
                                        } else {
                                            common.fillHistoryObjectWithEpoch(refObj.dtEntry, dashboardObj, deviceToBeDeleted.p.toLowerCase(), deviceToBeDeleted.pv + "." + deviceToBeDeleted.av + "." + history.refname + "." + deviceToBeDeleted.cc + "." + deviceToBeDeleted.cty + ".ru");
                                        }

                                        common.db.collection('timely_dashboard').update({
                                            '_id': id
                                        }, {
                                            '$inc': dashboardObj
                                        }, {
                                            'upsert': true
                                        }, function(err, response) {
                                            if (err) {
                                                logger.error('err : ' + JSON.stringify(err));
                                            } else {
                                                refObj.dtEntry = parseInt(refObj.dtEntry / 1000);
                                                // manage campaign aggregate collection
                                                common.manageCampaignAggregate(deviceToBeDeleted.p.toLowerCase(), refObj, params.qstring.app_id, diff);
                                                // call uninstall event
                                                deviceToBeDeleted.app_id = params.qstring.app_id;
                                                deviceToBeDeleted.key = "uninstall";
                                                commonEvent.sendEvent(deviceToBeDeleted);
                                                // check active user sc boundary
                                                common.manageActiveSessionUser(params.qstring.app_id, deviceToBeDeleted._id, -1, function(err, data) {
                                                    if (data) {
                                                        // manage user sc
                                                        if (data.scObj) {
                                                            common.db.collection('timely_users_session').update({
                                                                '_id': data.scId
                                                            }, {
                                                                '$inc': data.scObj
                                                            }, {
                                                                'upsert': true
                                                            }, function(err, response) {
                                                                if (err) {
                                                                    logger.error('timely_users_session sc err:' + JSON.stringify(err))
                                                                }
                                                            });
                                                        }
                                                        // manage user session average length
                                                        if (data.aslObj) {
                                                            common.db.collection('timely_users_session').update({
                                                                '_id': data.aslId
                                                            }, {
                                                                '$inc': data.aslObj
                                                            }, {
                                                                'upsert': true
                                                            }, function(err, response) {
                                                                if (err) {
                                                                    logger.error('timely_users_session asl err:' + JSON.stringify(err))
                                                                }
                                                            });
                                                        }
                                                    }
                                                });

                                                //common.returnMessage(params, 200, 'Success');
                                                postback.prepareWebHookData(params.qstring.app_id, {
                                                    did: params.qstring.did,
                                                    timestamp: refObj.dtEntry
                                                }, 'uninstall', function(url, data) {
                                                    if (url && data) {
                                                        // send data through webhook
                                                        postback.webHook(params.qstring.app_id, url, data);
                                                    }
                                                });
                                            }
                                        });
                                    }
                                });
                            }
                        }
                    }

                    if (err) {
                        logger.error(err);
                    }
                });
            }
            if (err) {
                logger.error(err);
            }
        });

        if (params.qstring.template_id !== "xxxxx") {
            common.db.collection('templates_' + params.qstring.app_id).findOne({
                "isTemplateDeleted": "false",
                "_id": common.db.ObjectID(params.qstring.template_id)
            }, function(err, result) {
                if (!result) {
                    result = {};
                    common.returnOutput(params, result);
                } else {
                    common.db.collection('apps').findOne({
                        "isAppDeleted": "false",
                        "_id": common.db.ObjectID(params.qstring.app_id)
                    }, function(err, appDetail) {
                        if (appDetail) {
                            result.app_name = appDetail.name;
                            result.app_icon = appDetail.icon;
                            // result.blobContainerCDN = semusiConfig.blobContainerCDN.slice(0, -1);
                        let blob = semusiConfig.blobContainerCDN.slice(0, -1);
                        if(semusiConfig.uploadAssets[0]=="Azure"){
                            //incase of azure the blob is different and doesn't need slice it even needs append of appid
                            blob =  semusiConfig.blobContainerCDN + params.qstring.app_id 
                        }
                        result.blobContainerCDN =  blob
                            common.returnOutput(params, result);
                        } else {
                            common.returnOutput(params, result);
                        }
                    });
                }
            });
        } else {
            common.returnOutput(params, {});
        }
    };

    templateApi.getTemplatesByType = function(params) {
        let result = {};
        let page =  (params.qstring.page == undefined) ? 0 : params.qstring.page;
        let skip = page;
        let query = {"isTemplateDeleted": "false", "template_type": params.qstring.template_type};
        if(params.qstring.query){
        query = {"isTemplateDeleted": "false", "template_type": params.qstring.template_type, "template_name": { "$regex": ".*"+params.qstring.query+".*", "$options": "i"}}; //case insensitive
        }
        common.db.collection('templates_' + params.qstring.app_id).count(query, function(err, count) {
            //base query for without pagination
            const baseQuery = common.db.collection('templates_' + params.qstring.app_id).find(query).sort({ updatedAt: 1 });


            if (!params.qstring.pagination) {
                //without pagination
                baseQuery.toArray(function (err, result) {
                    // Process the result here
                    sendOutputResult(result)
                });
            } else {
                //with pagination i.e, skip and limit
                baseQuery.limit(100).skip(skip * 100).toArray(function (err, result) {
                    // Process the result here
                    sendOutputResult(result)
                });
            }

            //process result and send output
            function sendOutputResult(result){

                let data = { result:result, total_count: count };
                if(params.qstring.template_type == "EMAIL"){
                    //get email budget form db and return to frontend                     
                    common.db.collection('apps').findOne({ "_id": common.db.ObjectID(params.qstring.app_id) }, { email_Config: 1 }, (err, res) => {
                        if (!err && res && res.email_Config) {

                            if(res.email_Config.email_currency == "paise" ){

                                let Rupees = parseInt(res.email_Config.total_bdgt) / 100
                                data.total_email_budget = Rupees
                                common.returnOutput(params, data);

                            }else{

                                data.total_email_budget = parseInt(res.email_Config.total_bdgt) 
                                common.returnOutput(params, data);

                            }
                        }else{
                            common.returnOutput(params, data); 
                        }
                    })
                }else{
                    common.returnOutput(params, data);
                }
            }
        });    
    };
    
    // get all templates without any limit
    templateApi.getAllAppTemplates = function(params) {
        let result = {};
        let page =  (params.qstring.page == undefined) ? 0 : params.qstring.page;
        let skip = page;
        let query = {"isTemplateDeleted": "false", "template_type": params.qstring.template_type};
        if(params.qstring.query){
            query = {"isTemplateDeleted": "false", "template_type": params.qstring.template_type, "template_name": { "$regex": ".*"+params.qstring.query+".*"}};
        }

        common.db.collection('templates_' + params.qstring.app_id).count(query, function(err, count) {
            common.db.collection('templates_' + params.qstring.app_id).find(query).sort({updatedAt : 1}).toArray(function(err, result) {
                let data = { result:result, total_count: count };
                common.returnOutput(params, data);
            });
        });    
    };

    templateApi.getActiveCampaigns = function(params) {
        cacheApi.getKey('campaigns_' + params.qstring.app_id, function(err, result) {
            if (err || result == null) {
                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(err);
                        return common.returnOutput(params, retVal);
                    }
                    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) {
                                                logger.info(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,params.qstring.app_id);
                                                                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) {
                                            validateDelayedCampaign(obj, params, function(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) {
                                                campaignCommon.validateCampaignUser(params.qstring.app_id, item._id, params.qstring.device_id, function(error, isValidate, uData) {
                                                    logger.info("error, isValidate", error, 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.");
                            //Do something on error
                            return common.returnOutput(params, 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));
                            return common.returnOutput(params, retVal);
                        }
                    });
                });
            } else {
                logger.info("got in cache");
                let data = '';
                let lastPullTimeCamp = [];
                data = JSON.parse(result);
                if (params.qstring.lastPullTime) {
                    async.forEach(data, function(item, callback) {
                        if (item.delays) {
                            validateDelayedCampaign(item, params, function(error, campData, status) {
                                if (!error && 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)) {

                                // validate live event and check user is able or not for this campaign
                                if (item.aud && item.aud.what && item.aud.what.length > 0) {
                                    campaignCommon.validateCampaignUser(params.qstring.app_id, item._id, params.qstring.device_id, function(error, isValidate, uData) {
                                        // 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
                                    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));
                                    return common.returnOutput(params, lastPullTimeCamp);
                                }
                            }
                        } else {
                            return common.returnOutput(params, lastPullTimeCamp);
                        }
                    });
                } else {
                    let camps = [];
                    //data.forEach(function(item){
                    async.forEach(data, function(item, callback) {
                        // check delayed unit into campaign
                        if (item.delays) {
                            validateDelayedCampaign(item, params, function(error, campData, status) {
                                if (!error && campData) {
                                    // prepare live event property
                                    prepareLiveEvents(campData.e, item, true);
                                    camps.push(item);
                                } else if (status) {
                                    camps.push(item);
                                }

                                callback();
                            });
                        } else {
                            if (parseInt(item.ed) > (common.getCurrentEpochTime() * 1000)) {
                                // validate live event and check user is able or not for this campaign
                                if (item.aud && item.aud.what && item.aud.what.length > 0) {
                                    campaignCommon.validateCampaignUser(params.qstring.app_id, item._id, params.qstring.device_id, function(error, isValidate, uData) {
                                        // 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
                                    camps.push(item);
                                    callback();
                                }
                            } else {
                                callback();
                            }
                        }
                    }, function(err) {
                        return common.returnOutput(params, camps);
                    });
                }
            }
        });

    };

    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;
                }

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

    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;
    }
    // * @param key is the event of the campaign, while difference is the seconds upto the campaign end data
    // This function checks if there is already a value with this event in the redis
    //  if Event is found, it  checks the ttl from the redis with the differnce and update if the ttl is less  
    checkSetkeyAndExpire = function (key, difference) {
              cacheApi.checkExpireTime(key, function (ttl) {
                if (ttl == -2) {
                  // Set the event Key with time when No event Is Found
                  cacheApi.setKey(key, "true");
                  cacheApi.expire(key, difference);
                } else if (ttl == -1 || ttl < difference) {
                  // Update event Key with time only when event is found but TTL is less
                  cacheApi.expire(key, difference);
                }
              });
    };

    // * @param segmentInfo of Selected Campaign , cmpEd is endDate of campagin 
    // Saving Events From The Campaign in the Redis 
    setEventsAtRedis= function(segmentInfo,cmpEd,currentDate,app_Id){
           let segmenInfo= {}
            segmenInfo = JSON.parse(segmentInfo)
           let InfoEvents = segmenInfo.what
           let difference = (cmpEd - currentDate);
           for (let event in InfoEvents) {
                         // checks for Pubsub    
                   if(InfoEvents[event].pubsub!=undefined){

                         checkSetkeyAndExpire('materialize_'+app_Id+InfoEvents[event].pubsub.pub.eventKey,difference);
                         checkSetkeyAndExpire('materialize_'+app_Id+InfoEvents[event].pubsub.sub.eventKey,difference);
                   }
                     // checks for Pubsub Events In What
                 if (InfoEvents[event].operand != undefined) {

                        checkSetkeyAndExpire('materialize_'+app_Id+InfoEvents[event].operand,difference);
                 }
           }
    }

    templateApi.setActiveCampaign = function(params) {
        logger.info(`setActiveCampaign Started ${params.qstring.cid}`)
        let adminAcess = (params.member.global_admin) ? true : (params.member.user_role.admin) ? params.member.user_role.admin : []
       let managerAcess = (params.member.global_admin) ? true : (params.member.user_role.manager) ? params.member.user_role.manager : []
 if(params.member.global_admin || adminAcess.includes(params.qstring.app_id) || managerAcess.includes(params.qstring.app_id) ){
        let output = {};
        let argProps = {
                'ud': {
                    'required': false,
                    'type': 'Number'
                },
                'st': {
                    'required': false,
                    'type': 'String'
                }
            },
            activeCampaign = {};
        statsCamp = {}; //sync from 
        let currentDate = parseInt(moment().valueOf() / 1000);
        if (!(activeCampaign = common.validateArgs(params.qstring.args, argProps))) {
            common.returnMessage(params, 400, 'Not enough args');
            return false;
        }
        let pushCommand = "#CAMPAIGNUPDATE";
        if (activeCampaign.segmentname == semusiConfig.atRiskAudience) {
            pushCommand = "#ATRISK#" + params.qstring.cid;
        }

        statsCamp.st = activeCampaign.st;
        activeCampaign.c_checker = common.getMaskedEmail(params.member.email)

        cacheApi.deleteKey('campaigns_' + params.qstring.app_id, function(err, res) {
            logger.info("Redis key cleared. " + 'campaigns_' + params.qstring.app_id);
        });
        //delete campaign user cache
        cacheApi.deleteKey("campaignuser_" + params.qstring.app_id + "" + params.qstring.cid, function(err, res) {
            logger.info("Redis key cleared. " + "campaignuser_" + params.qstring.app_id + "" + params.qstring.cid);
        });
        //delete campaign event cache
        cacheApi.deleteKey("campaignevent_" + params.qstring.app_id + "" + params.qstring.cid, function(err, res) {
            logger.info("Redis key cleared. " + "campaignevent_" + params.qstring.app_id + "" + params.qstring.cid);
        });

        //delete campaign event cache
        cacheApi.deleteKey("deliver_user_" + params.qstring.app_id + "" + params.qstring.cid, function(err, res) {
            logger.info("Redis key cleared. " + "campaignevent_" + params.qstring.app_id + "" + params.qstring.cid);
        });

        activeCampaign.ud = moment().valueOf();
        /* sync from dev code comment facing delete issue active to draft */

      common.db.collection('apps').find({'_id':common.db.ObjectID(params.qstring.app_id)}).toArray(function(err,app){
          if(err){
              logger.info(`Error:-> Couln't find Appname for this appId`)
          }else{
        const clientName = app[0].name;
        const appKey = app[0].key;
        // Check if the campaign is activ
        const isActiveCampaign = activeCampaign.st === "ACTIVE";
        // Check if the campaign insertion into pg is enabled for this particular app
        const isPgEnabled = isCampaignIntoPgEnabled(app[0]);
        // Check if a  JSON file is present in the app
        common.db.collection('campaigns_' + params.qstring.app_id).find({
            "d": false,
            "st": "ACTIVE",
            "t": "FEEDBACK"
        }).toArray(function(err, result) {
            if (!err && (!result || result.length == 0)) {
                common.db.collection('campaigns_' + params.qstring.app_id).findOne({
                    '_id': common.db.ObjectID(params.qstring.cid)
                }, function(err, cmp) {
               const audienceId = cmp.aud; 
               common.db.collection('audiencesegment_' + params.qstring.app_id).findOne({'_id': common.db.ObjectID(audienceId)}, function(err, audObj) {
                if(err || audObj == null){
                    
                    logger.error(`Error in Finding Audience for this CampId${err} `)
                }else{
                    activeCampaign.segmentinfo = JSON.stringify(audObj.segmentinfo);
                    activeCampaign.segmentname = audObj.name; 
                    // Check if this is variant campaign
                    const isVariantEnabled = cmp.variant === true;
                    if ((cmp.ed/1000) < currentDate) {
                        output.status = "ERROR";
                        output.message = "Selected campaign's end date has passed current date.";
                        common.returnOutput(params, output);
                        return true;
                    }
                        // call function for saving events in redis
                     setEventsAtRedis(activeCampaign.segmentinfo,(cmp.ed/1000),currentDate,params.qstring.app_id);
                      // check certificates at the path and upload from redis 
                      campaignUtils.checkCertificates(params.qstring.app_id).then((result) => {
                        // if Campaign is active
                    if (isActiveCampaign) {
                        // CASE OF SCHEDULED CAMPAIGN WHOSE CURRENT DATA IS GREATER THAN START DATE
                        if ((cmp.sd - 5 * 60 * 1000) > currentDate * 1000) {
                            // If any of the following is true:
                                 // 1.  Campaign is enabled for this particular app 
                                 // 2.  OR It is a variant Campaign
                                 // 3   JSON file is present in the app
                            if ( isPgEnabled || isVariantEnabled) {
                                logger.info("Data inserted to PGSQL Queue")
                                psqlUtils.addCampaignSchedule(params.qstring.app_id, params.qstring.api_key, appKey, params.qstring.cid, uuid.v4(), cmp.sd, cmp.ed, cmp.t, cmp.time,cmp.fre.s);
                            }

                            output.status = "update pending";
                            output.message = "update campaign's start date is greater then current date.";
                            activeCampaign['updatedAt'] = common.getCurrentEpochTime();
                            activeCampaign.st = "PENDING";
                            statsCamp.st = "PENDING";
                            common.db.collection('campaigns_' + params.qstring.app_id).update({
                                '_id': common.db.ObjectID(params.qstring.cid)
                            }, {
                                $set: activeCampaign
                            }, {
                                safe: true
                            }, function(err, isOk) {
                                if (!err) {

                                      //updating stats campaign status campaign_statsdata_
                                      common.db.collection('campaign_statsdata_' + params.qstring.app_id).update({
                                        '_id': params.qstring.cid.toString()
                                    }, {
                                        $set: statsCamp
                                    }, {
                                        safe: true
                                    }, function(err, isOk) {
                                        if (!err) {
                                            logger.info("updated stats campaign status");
                                        }

                                    });

                                    logger.info("update campaign's start date is greater then current date.");
                                }
                            });

                            //Send mail to user for SBIC-DEV / SBIC-PROD
                            if(semusiConfig.activateCampaignAPPIDS.includes(params.qstring.app_id)){
                                if(activeCampaign.st == "ACTIVE" || activeCampaign.st == "PENDING") {
                                        common.db.collection('campaigns_' + params.qstring.app_id).findOne({
                                            '_id': common.db.ObjectID(params.qstring.cid)
                                        }, function(err, campaigns) {
                                            if (!err && campaigns) {
                                                let message = {
                                                    to: 'backend@appice.io',
                                                    bcc: [],
                                                    subject: 'IMPORTANT - '+clientName+' push Campaign activated, please start cron',
                                                    html: 'Hello Team,<br/><br/>' +
                                                        'Please Find the Campaign Id for UM that has been Activated successfully From AppICE Dashboard. <b><br/><br/> Campaign Name :'+ campaigns.nm + '<br/><br/> Campaign Id : ' + params.qstring.cid + '<br/><br/><br/>Appice Team, <br/> Server: <a href="' + common.config.cdnUrl + '" target="_blank">' + common.config.cdnUrl + '</a>'
                                                };
                                                mail.sendMail(message);
                                                
                                            } 
                                        });
                                    
                                  }
                            }
                            common.returnOutput(params, output);
                            return true;
                        }

                    }
                    let segmentinfo = (cmp.segmentinfo) ? JSON.parse(cmp.segmentinfo) : {};
                    activeCampaign['updatedAt'] = common.getCurrentEpochTime();
                    activeCampaign.active = true;
                    //during activation of Exisitng campaign where time will be in old format like [8,21]
                    if (Array.isArray(cmp.time)) {
                        activeCampaign.time = cmp.time.map(convertToTime);
                    } else {
                        activeCampaign.time = cmp.time.t.map(convertToTime);
                    }
                    
                    // if upon activating old Campaign which will have time as SPECIFICTIME , turn it into SPECIFIC 
                    activeCampaign.timeSelector = (cmp.timeSelector && cmp.timeSelector == "SPECIFICTIME") ? "SPECIFIC": cmp.timeSelector;

                    statsCamp.active = true;
                    common.db.collection('campaigns_' + params.qstring.app_id).findOneAndUpdate({
                        '_id': common.db.ObjectID(params.qstring.cid)
                    }, {
                        $set: activeCampaign
                    }, {
                        // To get the updated Document in the same Query 
                        returnOriginal: false, // ensures that the updated document is returned
                        sort: { '_id': -1 },  // sort by _id field in descending order to get the latest document
                    }, function(err, camp) {
                        if (!err) {

                          let cyType= semusiConfig.cyType
                          let typelist =  (semusiConfig.getAppType )? semusiConfig.getAppType.split(' '): [];
                          if(typelist.length > 0  && typelist.indexOf(camp.value.t) !== -1 && cyType==camp.value.cy){
                            if(activeCampaign.st=="DRAFT"){
                                logger.info(`Data will be Updated In Active Campaign Campid On Deactivation ${params.qstring.cid}`)
                                campaignUtils.setAllDataAtDb( params.qstring.app_id , cmp._id , params.qstring.api_key)
                            }else{
                               // Save Data In Mongo 
                               logger.info(`Data will be set in the Active Campaign Campid ${params.qstring.cid}`)
                                campaignUtils.setAllDataAtDb( params.qstring.app_id , cmp._id , params.qstring.api_key)
                            }
                          }
                            //updating stats campaign status campaign_statsdata_
                            common.db.collection('campaign_statsdata_' + params.qstring.app_id).update({
                                '_id': params.qstring.cid.toString()
                            }, {
                                $set: statsCamp
                            }, {
                                safe: true
                            }, function(err, isOk) {
                                if (!err) {
                                    logger.info("updated stats campaign status");
                                }
                            });

                            if (cmp.childIds && cmp.childIds.length > 0) {
                                params.childAppIds = [];
                                cmp.childIds.forEach(function(item) {
                                    if (item.approved && !item.deleted) {
                                        params.childAppIds.push(item.id);
                                    }
                                });
                            }

                            cmp.segmentinfo = activeCampaign.segmentinfo;
                            if (activeCampaign.segmentname == "Dynamic Audience Segment" && activeCampaign.st == "ACTIVE") {
                                logger.info("Dynamic Segment Initation Activated =>", activeCampaign.segmentname)
                                common.db.collection('members').findOne({
                                    api_key: params.qstring.api_key
                                }, function(err, members) {
                                    if (err) {
                                        logger.error(`members collection data not found!`);
                                    } else if (members) {
                                        let campEmailAdminId = members.email;
                                        common.db.collection('campaigns_' + params.qstring.app_id).findOne({
                                            '_id': common.db.ObjectID(params.qstring.cid)
                                        }, function(err, campaigns) {
                                            if (err) {
                                                logger.error(`Campaigns data not found!`);
                                            } else {
                                                let message = {
                                                    to: campEmailAdminId,
                                                    bcc: ['saurabh.singh@semusi.com', 'etisalat@appice.io'],
                                                    subject: 'Campaign UM Data Details',
                                                    html: 'Hello Team,<br/><br/>' +
                                                        'Please Find the Campaign Id for UM that has been Activated successfully From AppICE Dashboard. <b><br/><br/> Campaign Name : ' + campaigns.nm + '<br/><br/> Campaign Id : ' + params.qstring.cid + '<br/><br/><br/>Appice Team, <br/> Server: <a href="' + common.config.cdnUrl + '" target="_blank">' + common.config.cdnUrl + '</a>'
                                                };
                                                mail.sendMail(message);
                                                logger.info('Sending Email =>', campEmailAdminId)
                                            }
                                        });
                                    }
                                });
                                    // check campaign type is varaint or is Campaign inserted into pg enabled
                            }else if ( isPgEnabled || isVariantEnabled ) {
                                logger.info(`Data inserted to PGSQL Queue Either it is Enabled or its varaint Campid =====>${params.qstring.cid}===with templateid ${(cmp.tid) ? cmp.tid :"tid not found "}`)
                                  // if Campaign is activated
                                if( isActiveCampaign ){
                                    logger.info(`Campaign is Activated And Data will be Inserted In PGSQL Queue`)
                                    psqlUtils.addCampaignSchedule(params.qstring.app_id,params.qstring.api_key, appKey ,params.qstring.cid,uuid.v4(),cmp.sd, cmp.ed, cmp.t,cmp.time,cmp.fre.s);
                                }else{
                                      // if Campaign is deactived
                                    logger.info(`Campaign is Deactivated And Data will be Updated In PGSQL Queue`)
                                    psqlUtils.updateCampaignSchedule(params.qstring.app_id ,params.qstring.cid,);
                                }
                            } else {

                                // check campaign type
                                if (cmp.t != "Email" && cmp.t != "SMS") {
                                    let eventLength = 0
                                    cmp.segmentinfo = JSON.parse(cmp.segmentinfo);
                                    if (cmp.segmentinfo && cmp.segmentinfo.what && cmp.segmentinfo.what.length > 0 && activeCampaign.st == "ACTIVE") {
                                        eventLength = cmp.segmentinfo.what.length;
                                        if (cmp.delays && cmp.segmentinfo.what.length == 1) {
                                            cmp.segmentinfo.what.forEach(function(evt) {
                                                cmp.delaySec = (cmp.delayu == "Hours") ? (parseInt(cmp.delayi) * 60) * 60 : parseInt(cmp.delayi) * 60
                                                cacheApi.setKey(evt.operand + "_" + params.qstring.app_id, JSON.stringify(cmp));
                                            });
                                        }

                                        let stag = {};
                                        if (Object.keys(cmp.stag).length > 0) {
                                            stag = {
                                                "stag": cmp.stag,
                                                "st": activeCampaign.st
                                            };
                                        } else {
                                            stag = {
                                                "st": cmp.st
                                            };
                                        }
                                        let obj = {
                                            appid: params.qstring.app_id,
                                            campid: params.qstring.cid,
                                            audienceQuery: cmp.segmentinfo.q ? cmp.segmentinfo.q.replace("count(distinct did)", "distinct did, p,fcmid,gcmid") : cmp.segmentinfo,
                                            stag: stag,
                                            timestamp: activeCampaign['updatedAt'],
                                            key: "campaigns"
                                        }
                                        //dataProducer.writeDataDirectly(obj, {});
                                        cmp.segmentinfo = JSON.stringify(cmp.segmentinfo);
                                    } else {

                                        if (cmp.stag && Object.keys(cmp.stag).length > 0) {
                                            let stag = {
                                                "stag": cmp.stag,
                                                "st": activeCampaign.st
                                            };
                                        } else {
                                            let stag = {
                                                "st": cmp.st
                                            };
                                        }

                                        let obj = {
                                            appid: params.qstring.app_id,
                                            campid: params.qstring.cid,
                                            audienceQuery: cmp.segmentinfo.q ? cmp.segmentinfo.q.replace("count(distinct did)", "distinct did, p,fcmid,gcmid") : cmp.segmentinfo,
                                            stag: cmp.stag,
                                            timestamp: activeCampaign['updatedAt'],
                                            key: "campaigns"
                                        }
                                        dataProducer.writeDataDirectly(obj, {});
                                        cmp.segmentinfo = JSON.stringify(cmp.segmentinfo);
                                    }

                                    if (cmp.tid) {
                                        common.db.collection('templates_' + params.qstring.app_id).findOne({
                                            "isTemplateDeleted": "false",
                                            "_id": common.db.ObjectID(cmp.tid)
                                        }, function(err, temp) {
                                            logger.info(cmp.tid);
                                            if (err) {
                                                logger.error('error coming => ', err)
                                            }
                                            cmp['template'] = temp['template'];
                                            // inserting template object to the updated document of the campaign
                                            camp.value['template'] = temp['template'];
                                            output.result = cmp.tid;
                                                 // passing the updated  Document
                                                templateApi.silentPushForCampaignUpdate(params, camp.value, pushCommand, (activeCampaign.st == "ACTIVE"));
                                                //setting key in cache should be the additional step
                                                if (activeCampaign.st == "ACTIVE" && (eventLength == 0)) {
                                                    cmp.delaySec = (cmp.delayu == "Hours") ? (parseInt(cmp.delayi) * 60) * 60 : parseInt(cmp.delayi) * 60
                                                    cacheApi.addToSadd('delayed_campaigns', params.qstring.app_id + "_" + cmp._id, function(response) {
                                                        if (response) {
                                                            cacheApi.setKey(params.qstring.app_id + "_" + cmp._id, JSON.stringify({
                                                                camp: cmp,
                                                                appid: params.qstring.app_id
                                                            }));
                                                            cacheApi.expire(params.qstring.app_id + "_" + cmp._id, parseInt(cmp.delaySec));
                                                        }
                                                    });
                                                } else if (activeCampaign.st == "DRAFT") {
                                                    // remove memeber form delayed_campaigns list
                                                    cacheApi.removeMember("delayed_campaigns", params.qstring.app_id + "_" + cmp._id, function(error, result) {
                                                        if (error) {}
                                                        // remove campaign data key
                                                        cacheApi.deleteKey(params.qstring.app_id + "_" + cmp._id, function(error, response) {});
                                                    });

                                                    // delete key if campaign is in draft
                                                    if (eventLength > 0) {
                                                        cmp.segmentinfo = JSON.parse(cmp.segmentinfo);
                                                        if (cmp.delays && cmp.segmentinfo.what.length == 1) {
                                                            cmp.segmentinfo.what.forEach(function(evt) {
                                                                cacheApi.deleteKey(evt.operand + "_" + params.qstring.app_id, function(err, delRes) {});
                                                            });
                                                        }
                                                    }
                                                }


                                        })
                                    }
                                } else {
                                    if (activeCampaign.st == "ACTIVE") {
                                        logger.info('send ' + cmp.t);
                                        executeCamp.runCampaign(params.qstring.app_id);
                                    }
                                }

                            }


                            //Send mail to user for SBIC-DEV / SBIC-PROD
                            if(semusiConfig.activateCampaignAPPIDS.includes(params.qstring.app_id)){
                                if (activeCampaign.st == "ACTIVE" || activeCampaign.st == "PENDING") {
                                    common.db.collection('campaigns_' + params.qstring.app_id).findOne({
                                        '_id': common.db.ObjectID(params.qstring.cid)
                                    }, function(err, campaigns) {
                                        if (!err && campaigns) {
                                            let message = {
                                                to: 'backend@appice.io',
                                                bcc: [],
                                                subject: 'IMPORTANT - '+clientName+' push Campaign activated, please start cron',
                                                html: 'Hello Team,<br/><br/>' +
                                                    'Please Find the Campaign Id for UM that has been Activated successfully From AppICE Dashboard. <b><br/><br/> Campaign Name :' + campaigns.nm + '<br/><br/> Campaign Id : ' + params.qstring.cid + '<br/><br/><br/>Appice Team, <br/> Server: <a href="' + common.config.cdnUrl + '" target="_blank">' + common.config.cdnUrl + '</a>'
                                            };
                                            mail.sendMail(message);

                                        }
                                    });

                                }
                            }
                            if (activeCampaign.st == "ACTIVE") {
                            updateCampaign={
                                c_checker : activeCampaign.c_checker
                            }
                                common.db.collection("campaign_statsdata_" +params.qstring.app_id)
                                  .findOneAndUpdate(
                                    {
                                      _id:params.qstring.cid
                                    },
                                    { $set: updateCampaign },
                                    { safe: true },
                                    function (err, camp) {
                                      if(err){
                                        logger.error(`error In Updating checker Details in Campaign Statsdata=>> ${err}`);
                                      }
                                    }
                                  );      

                            }

                            output.status = "OK";
                            output.message = "Selected campaign is active now.";

                            common.returnOutput(params, output);
                            return true;
                        } else {
                            common.returnMessage(params, 500, 'Error activating campaign.');
                            return false;
                        }
                    });
                })
                    }
                })                    
                });
            } //This is to handle scenario where we have existing active feedback campaigns and we are trying to set one more active.
            else if (!err && result) {
                common.db.collection('campaigns_' + params.qstring.app_id).findOne({
                    '_id': common.db.ObjectID(params.qstring.cid)
                }, function(err, newCmp) {
                    const audienceId = newCmp.aud; 
                    common.db.collection('audiencesegment_' + params.qstring.app_id).findOne({'_id': common.db.ObjectID(audienceId)}, function(err, audObj) {
                     if(err || audObj == null){
                         logger.error(`Error in Finding Audience for this CampId${err} `)
                     }else{
                         activeCampaign.segmentinfo =JSON.stringify(audObj.segmentinfo);
                         activeCampaign.segmentname = audObj.name; 

                    if (newCmp.ed < currentDate && newCmp.st == "DRAFT") {
                        logger.info('in fourth')
                        output.status = "ERROR";
                        output.message = "Selected campaign's end date has passed current date.";
                        common.returnOutput(params, output);
                        return true;
                    }
                    let found = false;
                    result.forEach(function(item) {
                        if (((newCmp.sd >= item.sd && newCmp.sd <= item.ed) || (newCmp.ed >= item.sd && newCmp.ed <= item.ed)) && activeCampaign.st == "ACTIVE" && newCmp.t == "FEEDBACK") {
                            found = true;
                            output.status = "ERROR";
                            output.message = "Date range conflicts with one of the existing active campaigns.";
                            common.returnOutput(params, output);
                            return true;
                        }

                    });
                    if (!found) {
                        activeCampaign['updatedAt'] = common.getCurrentEpochTime();
                        common.db.collection('campaigns_' + params.qstring.app_id).update({
                            '_id': common.db.ObjectID(params.qstring.cid)
                        }, {
                            $set: activeCampaign
                        }, {
                            safe: true
                        }, function(err, isOk) {
                            if (!err) {
                                if (newCmp.childIds && newCmp.childIds.length > 0) {
                                    params.childAppIds = [];
                                    newCmp.childIds.forEach(function(item) {
                                        if (item.approved && !item.deleted) {
                                            params.childAppIds.push(item.id);
                                        }
                                    });
                                }
                                newCmp.segmentinfo = activeCampaign.segmentinfo;
                                // check campaign type
                                if (newCmp.t != "Email" && newCmp.t != "SMS") {
                                    newCmp.segmentinfo = JSON.parse(newCmp.segmentinfo);
                                    let eventLength = 0
                                    if (newCmp.segmentinfo && newCmp.segmentinfo.what && newCmp.segmentinfo.what.length > 0 && activeCampaign.st == "ACTIVE") {
                                        eventLength = newCmp.segmentinfo.what.length;
                                        if (newCmp.delays && newCmp.segmentinfo.what.length == 1) {
                                            newCmp.segmentinfo.what.forEach(function(evt) {
                                                newCmp.delaySec = (newCmp.delayu == "Hours") ? (parseInt(newCmp.delayi) * 60) * 60 : parseInt(newCmp.delayi) * 60
                                                cacheApi.setKey(evt.operand + "_" + params.qstring.app_id, JSON.stringify(newCmp));
                                            });
                                        }

                                        newCmp.segmentinfo = JSON.stringify(newCmp.segmentinfo);
                                        let obj = {
                                            appid: params.qstring.app_id,
                                            campid: params.qstring.cid,
                                            segmentinfo: newCmp.segmentinfo,
                                            timestamp: activeCampaign['updatedAt'],
                                            key: "campaigns"
                                        }
                                        dataProducer.writeDataDirectly(obj, {});
                                    } else {
                                        newCmp.segmentinfo = JSON.stringify(newCmp.segmentinfo);
                                        let obj = {
                                            appid: params.qstring.app_id,
                                            campid: params.qstring.cid,
                                            segmentinfo: newCmp.segmentinfo,
                                            timestamp: activeCampaign['updatedAt'],
                                            key: "campaigns"
                                        }
                                        dataProducer.writeDataDirectly(obj, {});
                                    }
                                    if (!newCmp.delays || (eventLength > 1)) {
                                        templateApi.silentPushForCampaignUpdate(params, newCmp, pushCommand, (activeCampaign.st == "ACTIVE")); // if need change last param with false
                                    } else {
                                        if (activeCampaign.st == "ACTIVE" && (eventLength == 0)) {
                                            newCmp.delaySec = (newCmp.delayu == "Hours") ? (parseInt(newCmp.delayi) * 60) * 60 : parseInt(newCmp.delayi) * 60
                                            cacheApi.addToSadd('delayed_campaigns', params.qstring.app_id + "_" + newCmp._id, function(response) {
                                                if (response) {
                                                    logger.info('delayed_campaigns in template ' + JSON.stringify(response));
                                                    cacheApi.setKey(params.qstring.app_id + "_" + newCmp._id, JSON.stringify({
                                                        camp: newCmp,
                                                        appid: params.qstring.app_id
                                                    }));
                                                    cacheApi.expire(params.qstring.app_id + "_" + newCmp._id, parseInt(newCmp.delaySec));
                                                }
                                            });
                                        } else if (activeCampaign.st == "DRAFT") {
                                            // remove memeber form delayed_campaigns list
                                            cacheApi.removeMember("delayed_campaigns", params.qstring.app_id + "_" + newCmp._id, function(error, result) {
                                                if (error) {}
                                                // remove campaign data key
                                                cacheApi.deleteKey(params.qstring.app_id + "_" + newCmp._id, function(error, response) {});
                                            });

                                            // delete key if campaign is in draft
                                            if (eventLength > 0) {
                                                newCmp.segmentinfo = JSON.parse(newCmp.segmentinfo);
                                                if (newCmp.delays && newCmp.segmentinfo.what.length == 1) {
                                                    newCmp.segmentinfo.what.forEach(function(evt) {
                                                        cacheApi.deleteKey(evt.operand + "_" + params.qstring.app_id, function() {});
                                                    });
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    if (newCmp.st == "ACTIVE") {
                                        executeCamp.runCampaign(params.qstring.app_id);
                                    }
                                }


                                //Send mail to user for SBIC-DEV / SBIC-PROD
                                if(semusiConfig.activateCampaignAPPIDS.includes(params.qstring.app_id)){
                                    if (activeCampaign.st == "ACTIVE" || activeCampaign.st == "PENDING") {
                                        common.db.collection('campaigns_' + params.qstring.app_id).findOne({
                                            '_id': common.db.ObjectID(params.qstring.cid)
                                        }, function(err, campaigns) {
                                            if (!err && campaigns) {
                                                let message = {
                                                    to: 'backend@appice.io',
                                                    bcc: [],
                                                    subject: 'IMPORTANT - '+clientName+' push Campaign activated, please start cron',
                                                    html: 'Hello Team,<br/><br/>' +
                                                        'Please Find the Campaign Id for UM that has been Activated successfully From AppICE Dashboard. <b><br/><br/> Campaign Name :' + campaigns.nm + '<br/><br/> Campaign Id : ' + params.qstring.cid + '<br/><br/><br/>Appice Team, <br/> Server: <a href="' + common.config.cdnUrl + '" target="_blank">' + common.config.cdnUrl + '</a>'
                                                };
                                                mail.sendMail(message);

                                            }
                                        });

                                    }
                                }

                                output.status = "OK";
                                output.message = "Selected campaign is active now.";

                                common.returnOutput(params, output);
                                return true;
                            } else {
                                common.returnMessage(params, 500, 'Error activating campaign.');
                                return true;
                            }
                        });
                    }
                }
            })  
                });
            } else {
                common.returnMessage(params, 500, 'Error activating campaign.');
            }
        });
         }
     })
    }else{
        common.returnOutput(params, null);
      }

        return true;
    };

    templateApi.deleteCampaign = function(params) {
        let adminAcess = (params.member.global_admin) ? true : params.member.user_role.admin;
        let managerAcess = (params.member.global_admin) ? true : params.member.user_role.manager;

    if(params.member.global_admin || adminAcess.includes(params.qstring.app_id) || managerAcess.includes(params.qstring.app_id)){
        let argProps = {
                'isdelete': {
                    'required': true,
                    'type': 'Boolean'
                },
                'ud': {
                    'required': false,
                    'type': 'Number'
                }
            },
            delCmp = {};

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

        cacheApi.deleteKey('campaigns_' + params.qstring.app_id, function(err, res) {});

        delCmp.ud = moment().valueOf(); //Server gmt time.
        delCmp['updatedAt'] = common.getCurrentEpochTime();
        common.db.collection('campaign_statsdata_' + params.qstring.app_id).update({'_id':params.qstring.cid},{$set: delCmp},{safe: true}, function(err, result) {
            if (!err) {
                delCmp['d'] = delCmp['isdelete'];
                delete delCmp['isdelete'];
                common.db.collection('campaigns_' + params.qstring.app_id).update({
                    '_id': common.db.ObjectID(params.qstring.cid)
                }, {
                    $set: delCmp
                }, {
                    safe: true
                }, function(err, isOk) {
                    if (!err) {
                        logger.info(`Data will be Removed From Active Campaign Campid On Deletion of camp ${params.qstring.cid}`)
                        campaignUtils.removeAllDataFromDb( params.qstring.app_id  , params.qstring.cid, params.qstring.api_key )
                        common.returnMessage(params, 200, 'Success');
                    } else {
                        common.returnMessage(params, 500, 'Error deleting campaigns.');
                    }
                });
            } else {
                common.returnMessage(params, 500, 'Error deleting campaigns.');
            }
        });
    }else{
        common.returnOutput(params, null);
      }

        return true;
    };

// Templates Code starts 

// delete campaign starts
templateApi.deleteTemplate = function(params) {

    let adminAcess = (params.member.global_admin) ? true : params.member.user_role.admin;
    let managerAcess = (params.member.user_role.manager) ? params.member.user_role.manager : adminAcess ;

if(params.member.global_admin || adminAcess.includes(params.qstring.app_id) || managerAcess.includes(params.qstring.app_id)){
    let argProps = {
            'isTemplateDeleted': {
                'required': true,
                'type': 'Boolean'
            },
            'ud': {
                'required': false,
                'type': 'Number'
            }
        },
        delTemp = {};

        // validate args
    if (!(delTemp = common.validateArgs(params.qstring.args, argProps))) {
        common.returnMessage(params, 400, 'Not enough args');
        return false;
    }
    delTemp.ud = moment().valueOf(); //Server gmt time.
    delTemp['updatedAt'] = common.getCurrentEpochTime();
    delete delTemp.id;

    common.db.collection('templates_' + params.qstring.app_id).update({'_id': common.db.ObjectID(params.qstring.args.t_id)},{ $set: delTemp},{safe: true}, function(err, isOk) {
        if (!err) {
            common.returnMessage(params, 200, 'Success', delTemp);
        } else {
            common.returnMessage(params, 500, 'Error deleting Templates.');
        }
    });

}else{
    common.returnOutput(params, null);
}

 return true;
};

// template code ends
    templateApi.updateResponse = function(params) {

        //This function is obselete now.
        common.returnOutput(params, {});
    };

    /*
    For updating the bulk responses of multiple campaigns
    sent by same device
    */
    templateApi.updateBulkResponse = function(params) {
        let argProps = {
                'app_id': {
                    'required': true,
                    'type': 'String'
                },
                'did': {
                    'required': true,
                    'type': 'String'
                },
                'response': {
                    'required': true,
                    'type': 'JSON'
                }
            },
            campaignResponse = {};

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

        let retVal = {};

        //iterate through the responses
        campaignResponse.response.forEach(function(entry) {
            responseItem = [entry];
            campaignResponse.timestamp = common.getCurrentEpochTime();
            responseObj = {};
            responseObj.did = campaignResponse.did;
            responseObj.campid = entry.campid;
            responseObj.appid = campaignResponse.app_id
            responseObj.response = responseItem;
            if (responseObj.campid != "xxxxx") {
                responseObj['createdAt'] = common.getCurrentEpochTime();
                common.db.collection('campaign_responses').insert(responseObj, function(err, resObj) {
                    //responseObj._id = resObj[0]._id;
                });
            }

        });

        retVal.result = "true";
        common.returnOutput(params, retVal);

    };

    templateApi.getAtRiskAudienceSegmentId = function(params) {
        common.db.collection('audiencesegment_' + params.qstring.app_id).findOne({
            "name": semusiConfig.atRiskAudience
        }, function(err, result) {
            if (err) {
                common.returnMessage(params, 500, 'Error while fetching at risk segment.');
            } else if (result) {
                common.returnMessage(params, 200, result._id);
            } else {
                let seg = {
                    "name": semusiConfig.atRiskAudience,
                    "createdate": common.getCurrentEpochTime() * 1000,
                    "type": "system",
                    "range": "NA",
                    "segmentinfo": {
                        "who": [{
                            "operand": "atrisk",
                            "operator": "eq",
                            "value": true,
                            "category": "Device"
                        }]
                    },
                    "isDeleted": false
                };

                seg['createdAt'] = common.getCurrentEpochTime();
                common.db.collection('audiencesegment_' + params.qstring.app_id).insert(seg, function(err, result) {
                    common.returnMessage(params, 200, result[0]._id);
                });
            }
        })
    }

    templateApi.updateResponseTest = function(params) {
        let argProps = {
                'app_id': {
                    'required': true,
                    'type': 'String'
                },
                'did': {
                    'required': true,
                    'type': 'String'
                },
                'campid': {
                    'required': true,
                    'type': 'String'
                },
                'response': {
                    'required': true,
                    'type': 'JSON'
                },
                'uninstallTime': {
                    'required': true,
                    'type': 'Number'
                }
            },
            campaignResponse = {};

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

        //check if this campaign is yet alive
        common.db.collection('campaigns_' + campaignResponse.app_id).findOne({
            '_id': common.db.ObjectID(campaignResponse.campid)
        }, function(err, campaignObj) {
            if (!err) {
                //Mark device as uninstalled
                common.db.collection('app_users' + campaignResponse.app_id).findOne({
                    'did': campaignResponse.did
                }, function(err, delDevice) {
                    if (delDevice) {
                        //pick the last referrral only
                        historyCount = delDevice.history.length;
                        refObj = {};
                        refObj.refname = delDevice.history[historyCount - 1].refname;
                        refObj.type = "U";
                        refObj.dtEntry = campaignResponse.uninstallTime;
                        let updatedAt = common.getCurrentEpochTime();
                        common.db.collection('app_users' + campaignResponse.app_id).update({
                            'did': campaignResponse.did
                        }, {
                            '$addToSet': {
                                "history": refObj
                            },
                            '$set': {
                                'updatedAt': updatedAt
                            }
                        });
                    }
                    if (err) {
                        logger.error(err);
                    }
                });

                campaignResponse.intime = common.getCurrentEpochTime();
                campaignResponse['createdAt'] = common.getCurrentEpochTime();
                common.db.collection('response_' + campaignResponse.campid).insert(campaignResponse, function(err, resObj) {
                    campaignResponse._id = resObj[0]._id;
                    common.returnOutput(params, campaignResponse);

                });
            } else {
                common.returnMessage(params, 404, 'Campaign not found');

            }
        });
    };





    // while download list of campaigns or a single campaign they work on this function 
    templateApi.downloadCampaignDataCommon = async function (params) {

        //get app data
        const app = await common.retrieveAppSettings(params.qstring.app_id);
        //get default columns
        const DEFAULT_COLUMN = validate.getColumns("DEFAULT_COLUMN")

        //get column configuration if it is in app , else use default
        const columnStatus = (app.campaign_columns) ? app.campaign_columns : DEFAULT_COLUMN
        // Define an array of column objects To handle the csv Header 
        const columnObjects = [
            { key: 'receivedTo', label: 'Received' },
            { key: 'clickedTo', label: 'Clicked' },
            { key: 'impressionRate', label: 'Impression Rate' },
            { key: 'ctr', label: 'CTR' },
            { key: 'conversion', label: 'Conversion' },
        ];
        // Initialize an array to store CSV header fields
        const csvFields = ["Type", "Name", common.sanitizeString('Audience Segement'), common.sanitizeString('Reachable Audience'), "Sent"];

        // Dynamically add Csv Headers based on columnStatus objects
        let csvData = columnObjects.reduce((fields, column) => {
            if (columnStatus[column.key]) {
                fields.push(common.sanitizeString(column.label));
            }
            return fields;
        }, csvFields).concat(common.sanitizeString('Start Date'), common.sanitizeString('End Date')).join(',');


        let match = {
            isdelete: false
        };

        // if need report for any status 
        if (params.qstring.status) {
            if (params.qstring.status == "ACTIVE") {
                match.st = {
                    $in: ["ACTIVE", "PENDING"]
                };
            } else {
                match.st = params.qstring.status;
            }
        }
        // if need report for single cid
        if(params.qstring.cid){
            match._id = params.qstring.cid;
        }
        //for transactional campaigns
        if(params.qstring.cy){
            match.cy = "t";
        }else{
            match.cy = {
                $in: ["g", "e",'u']
            };
        }
        if (params.qstring.search != '' && params.qstring.searchValue != undefined) {

            let searchValue = params.qstring.searchValue;
            match.$or = [
                { "cname": { $regex: searchValue, $options: 'i' } },  // Case-insensitive regex match
                { "segmentname": { $regex: searchValue, $options: 'i' } }  // Case-insensitive regex match
            ];

        }

        const start = parseInt(params.qstring.start) || 0;  // DataTables start parameter
        const length = parseInt(params.qstring.length) || 10;  // DataTables length parameter


        common.db.collection('campaign_statsdata_' + params.qstring.app_id)
            .find(match)
            .sort({ sd: -1 })
            .skip(start)
            .limit(length)
            .toArray( async function (err, campaigns) {


                // common.db.collection('campaign_statsdata_' + params.qstring.app_id).find(match).toArray(function(err, campaign) {
                if (err) {
                    logger.error("Error in campaign data csv", err);
                    common.returnOutput(params, err);
                }
                const campaign =  await updateCampaignsDaywiseStatus(params , campaigns, params.qstring.app_id, params.qstring.sd, params.qstring.ed )
                if (campaign) {
                    for (let i = 0; i < campaign.length; i++) {
                        //calculate CTR
                        if (campaign[i].clickedTo && campaign[i][semusiConfig.campaignColumn]) {
                            campaign[i].ctr = parseFloat((campaign[i].clickedTo / campaign[i][semusiConfig.campaignColumn]) * 100).toFixed(2) + "%";
                        } else {
                            campaign[i].ctr = 0;
                        }
                        //set the default value in case its not found Or undefined 
                        campaign[i].reach = (campaign[i].Campaign_Reach) ? campaign[i].Campaign_Reach : 0;
                        campaign[i].pushedTo = (campaign[i].Campaign_PushedTo) ? campaign[i].Campaign_PushedTo : 0;
                        campaign[i].receivedTo = (campaign[i].Campaign_ReceivedTo) ? campaign[i].Campaign_ReceivedTo : 0;
                        campaign[i].clickedTo = (campaign[i].Campaign_ClickedTo) ? campaign[i].Campaign_ClickedTo : 0;
                        campaign[i].ctr = (campaign[i].clickedTo && campaign[i].reach)
                                            ? `${((campaign[i].clickedTo / campaign[i].reach) * 100).toFixed(2)}%`
                                            : '0%';

                        campaign[i].impressionRate = (campaign[i].receivedTo && campaign[i].pushedTo)
                                            ? `${((campaign[i].receivedTo / campaign[i].pushedTo) * 100).toFixed(2)}%`
                                            : '0%';

                        //Calcuate Impression Rate 
                        if (campaign[i].receivedTo && campaign[i].pushedTo) {
                            campaign[i].impressionRate = parseFloat((campaign[i].receivedTo / campaign[i].pushedTo) * 100).toFixed(2) + "%";
                        } else if (campaign[i].receivedTo === 0 || campaign[i].pushedTo === 0) {
                            campaign[i].impressionRate = 0;
                        }
                        // Concatenate these variables without any condition
                        csvData += "\r\n" + campaign[i].type + "," + common.sanitizeString(campaign[i].cname) + "," + common.sanitizeString(campaign[i].segmentname) + "," + campaign[i].reach + "," + campaign[i].pushedTo;

                        // Iterate over the keys in columnStatus and concatenate the variables based on their values
                        for (const key in columnStatus) {
                            if (columnStatus.hasOwnProperty(key) && columnStatus[key]) {
                                // Check if the key exists in campaign[i] before concatenating
                                if (campaign[i].hasOwnProperty(key)) {
                                    csvData += "," + campaign[i][key];
                                } else {

                                    csvData += "," + 0;
                                }
                            }
                        }

                        // Concatenate start date and end date regardless of any conditions
                        csvData += "," + common.sanitizeString(common.getEpochToDateWithTime(campaign[i].sd, true)) + "," + common.sanitizeString(common.getEpochToDateWithTime(campaign[i].ed, true));
                    }
                    common.returnCSV(params, csvData.replace('undefined', ''));
                }

            });
    };

    //return template cdata
    templateApi.getCustomData = function(params) {
        if (params.qstring.tid) {
            if (params.qstring.ct && params.qstring.ct == 'trans') {
                common.db.collection("transaction_camp_" + params.qstring.app_id).findOne({
                    _id: common.db.ObjectID(params.qstring.tid)
                }, function(err, tool) {
                    if (!err && tool) {
                        common.returnOutput(params, tool.payload.cdata);
                    } else {
                        common.returnOutput(params, {});
                    }
                });
            } else {
                common.db.collection("templates_" + params.qstring.app_id).findOne({
                    _id: common.db.ObjectID(params.qstring.tid)
                }, function(err, tool) {
                    if (!err && tool) {
                        common.returnOutput(params, tool.template.cdata);
                    } else {
                        common.returnOutput(params, {});
                    }
                });
            }

        } else {
            common.returnOutput(params, {});
        }
    }

    async function populateWebPushObject(template, data, params) {
        data.title = template.title,
            data.message = template.message,
            data.eurl = (template.externalUrl) ? template.externalUrl : '';
            let blob = semusiConfig.blobContainerCDN;
            
            if(semusiConfig.uploadAssets[0]=="Azure"){
                blob += params.qstring.app_id 
            }
            if(common.isValidUrl(template.icon)){
                data.icon =  template.icon;
            }else{
                data.icon =  (template.icon == "") ? "" : (template.icon.includes(validate.getCDN("azureCDN"))) ? blob + template.icon.substring(template.icon.lastIndexOf('/')) : blob+template.icon ;
            }
            if(common.isValidUrl(template.extendedImage)){
                data.eni =  template.extendedImage;
            }else{
                // we have to check if template.extendedImage exist or not 
                data.eni =  (template.eni == "") ? "" : (template.extendedImage &&  template.extendedImage.includes(validate.getCDN("azureCDN"))) ? blob + template.extendedImage.substring(template.extendedImage.lastIndexOf('/')) : blob+template.extendedImage ;
            }
            data.action = (template.actions) ? template.actions : undefined;
        if (template.cdata) {
            let cdataObj = await common.getCdataObj(template.cdata)
            data.cdata = cdataObj;
        }
             
    }
    async function sendWebPush(match, template, appKeys, params) {

        let paramArgs = JSON.parse(params.qstring.args.segmentinfo);
        let deviceId = paramArgs['who'][0]['value']
        let temp = {};
        temp.title = template.title,
        temp.message = template.message,
        temp.url = (template.url) ? template.url : '',
        temp.ttl = (template.ttl) ? template.ttl : '',
        temp.icon = template.icon,
        temp.image = (template.icon) ? template.icon : '',
        temp.badge = (template.icon) ? template.icon : '',
        temp.tag = (template.tag) ? template.tag : '';
        // return false;
       // let appKeys = await getWebPushAppKey(params)

        common.db.collection('app_users' + params.qstring.app_id).findOne({
            'did': deviceId
        }, function(err, deviceInfo) {
            if (err) {
                logger.error('hey error coming for retreving data info ==>>', err)
            }
            initiateWebPush(temp, deviceInfo, appKeys)
        })
    }

    function initiateWebPush(data, deviceInfo, appKeys) {
        const payload = data;
        const pushSubscription = {
            endpoint: deviceInfo.fcmid.endpoint,
            keys: {
                p256dh: deviceInfo.fcmid.keys.p256dh,
                auth: deviceInfo.fcmid.keys.auth
            }
        };

        const pushPayload = JSON.stringify(payload);

        const pushOptions = {
            vapidDetails: {
                subject: appKeys.subject,
                privateKey: appKeys.privateKey,
                publicKey: appKeys.publicKey
            },
            TTL: payload.ttl,
            headers: {}
        };

        webPush.sendNotification(
            pushSubscription,
            pushPayload,
            pushOptions
        ).then((value) => {
            logger.info('webpush fired ===>>>', value)
        }).catch((err) => {

            logger.error('web push error ===>>', err)
        });
    }

    async function populatePushObject(template, data, params) {
        data.nh = template.notificationHeader;
        data.nd = template.notificationDescription;
        //add image base url using getBaseUrl function 
        if(common.isValidUrl(template.notificationImage)){
            data.ni =  template.notificationImage;
        }else{
            data.ni =  (template.notificationImage != "") ? semusiConfig.blobContainerCDN.slice(0, -1)+template.notificationImage : ""  ;
        }
        data.end = template.expandedNotificationDescription;
        //add image base url using getBaseUrl function 
        if(common.isValidUrl(template.expandedNotificationImage)){
            data.eni =  template.expandedNotificationImage;
        }else{
            data.eni = (template.expandedNotificationImage != "") ? semusiConfig.blobContainerCDN.slice(0, -1)+template.expandedNotificationImage : ""  ;
        }
        
        if(template.ty != '' && template.ty != undefined){
            data.ty = template.ty ? template.ty : "";
            data.and = template.and ? template.and : "";
        }
        
        data.eurl = template.externalUrl;
        data.et = template.notificationType;
        data.actions = template.actions;
        data.sound = template.sound;
        data.vibrate = template.vibrate;
        data.badge = template.badge;
        let templateIdDetails = data.tid;
        if(data.tid != undefined){
            templateIdDetails = template._id;
        }
        let cdataObj = '';
        if (template.cdata) {
            cdataObj = await common.getCdataObj(template.cdata)
            cdataObj.dt = common.getCurrentEpochTime();
        }
           
        data.cdata = cdataObj;
    }

    async function populateInAppObject(template, data,app_id) {
        //getting the blob
        let blob = semusiConfig.blobContainerCDN
        //incase of azure append appid 
        if(semusiConfig.uploadAssets[0]=="Azure"){
            blob += app_id 
        }
        let azureCdn = "/appice-prod-campaign-images/uploads/campaignFiles/campaignimages";
        data.mode = template.view_mode;
        data.nh = template.notificationHeader;
        data.nd = template.notificationDescription;
            // Notification image
    if(template.notificationImage){
        if(common.isValidUrl(template.notificationImage)|| template.notificationImage == undefined){
            data.ni =  template.notificationImage;
        }else{
            //for existing templates whose  notificationImage contains half blobContainerCdn
            template.notificationImage = ( template.notificationImage.includes(azureCdn) ) ? template.notificationImage.substring(template.notificationImage.lastIndexOf("/")) : template.notificationImage
            data.ni =  (template.notificationImage != "") ? blob+ template.notificationImage : ""  ;
        }
    }else{
        data.ni = template.ni
    }
    //Icon image
    if(template.iconImage){
        if(common.isValidUrl(template.iconImage)|| template.iconImage == undefined){
            data.ii =  template.iconImage;
        }else{
            //for existing templates whose  notificationImage contains half blobContainerCdn
            template.iconImage = ( template.iconImage.includes(azureCdn) ) ? template.iconImage.substring(template.iconImage.lastIndexOf("/")) : template.iconImage
            data.ii =  (template.iconImage != "") ? blob+ template.iconImage : ""  ;
        }
    }else{
        data.ii = template.ii
    }
        data.aurl = (template.actionUrl) ? template.actionUrl:template.aurl ;
            data.et = (template.notificationType) ? template.notificationType : template.et;

        // If action title is empty don't pass at
        data.at = (template.actionTitle != null && template.actionTitle != '') ? template.actionTitle : undefined
        data.sa = template.showAction;
        data.cdata = (template.cdata) ? template.cdata : {} ;
		// decoding the values  for each key of cdata 
        if(template.cdata){
            let cdataObj = await common.getCdataObj(template.cdata)
            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);
            });
        }
    }

    async function getJSON(url) {
        const response = await fetch(url);
        const body = await response.json();
        return body;
    }


/**
 * Checks if the campaign insertion into PostgreSQL is enabled for the given app.
 * 
 * @param {app} app - The Array which has App document in the first Element.
 * @returns {boolean} - Returns true if campaign insertion into PostgreSQL is enabled, otherwise false.
 */
    function isCampaignIntoPgEnabled(app){
      return app && app.features && ('cmpInsertInToPG' in app.features) && !app.features.cmpInsertInToPG
    }
/**
 * Checks if a JSON file is present based on the application mode and certificate availability.
 * @param {Object} app - An object containing application details.
 * @param {boolean} app.isappmodeproduction - Indicates whether the application is in production mode.
 * @returns {boolean} - Returns true if the JSON file push is enabled from setting,  false otherwise.
 */

    templateApi.silentPushForCampaignUpdate = function(params, activeCampaign, pushCommand, setActive, isDid) {
        logger.info(`Push Initiated  In Campaign Update===============`)
        let segmentinfo;
        try {
            if (activeCampaign) {
                segmentinfo = JSON.parse(activeCampaign.segmentinfo);
            }
        } catch (e) {
            logger.error(e.message);
        }
        let payload = {
            camp_id: (activeCampaign && activeCampaign._id) ? activeCampaign._id : false,
            tid: (activeCampaign && activeCampaign.tid) ? activeCampaign.tid : false,
            aud: segmentinfo,
        };

        if (activeCampaign && activeCampaign.d == false && activeCampaign.template) {
            switch (activeCampaign.t) {
                case "PUSH":
                    populatePushObject(activeCampaign.template, payload, params);
                    break;
                case "IN-APP":
                    populateInAppObject(activeCampaign.template, payload,params.qstring.app_id);
                    break;
                case "RATING":
                    populateRatingObject(activeCampaign.template, payload);
                    break;
                case "SURVEY":
                    let q = [];
                    populateSurveyObject(activeCampaign.template, q);
                    payload.q = q;
                    break;
                case "WEB_PUSH":
                    populateWebPushObject(activeCampaign.template, payload, params);
                    break;
            }
        }

        let app_id = params.qstring.app_id;
        let cid = params.qstring.cid;
        common.db.collection('apps').findOne({
            "_id": common.db.ObjectID(app_id)
        }, async function(err, result) {
            if (!err && result !== undefined) {
                // initialize fcm credentials
                let linkingField = result.linkingField;
                //dev mode data for push
                let devData = {
                    validAPN: true,
                    validGCM: true
                };
                let prodData = {
                    validAPN: true,
                    validGCM: true
                };
                let cert3 = "";
                if (result.isappmodeproduction) {
                    cert3 = result.ios_cert3;
                } else if ((result.isappmodeproduction === undefined || result.isappmodeproduction == false)) {
                    cert3 = result.dev_ios_cert3;
                }
                // check apple certificate. if not exists on local machine then download from s3
                 common.checkAppleCertificate(app_id, cert3, async function(resCertificate) {
                    if (resCertificate) {
                        if (result.dev_ios_cert3 && result.dev_ios_cert3 != "") {
                            devData.certFileName = path.join(__dirname, "../../../frontend/express/public/appcertificates/" + app_id + "/" + result.dev_ios_cert3);
                        } else {
                            devData.validAPN = false;
                        }
                        if (result.dev_ios_cert_pem && result.dev_ios_cert_pem != "") {
                            devData.pemCertFileName = path.join(__dirname, "../../../frontend/express/public/appcertificates/" + app_id + "/" + result.dev_ios_cert_pem);
                        } else {
                            devData.validAPN = false;
                        }
                        if (result.dev_ios_cert_key && result.dev_ios_cert_key != "") {
                            devData.pemCertKeyName = path.join(__dirname, "../../../frontend/express/public/appcertificates/" + app_id + "/" + result.dev_ios_cert_key);
                        } else {
                            devData.validAPN = false;
                        }
                        if (result.dev_ios_keyid && result.dev_ios_keyid != "") {
                            devData.dev_ios_keyid = result.dev_ios_keyid;
                        } else {
                            devData.validAPN = false;
                        }
                        if (result.dev_ios_teamid && result.dev_ios_teamid != "") {
                            devData.dev_ios_teamid = result.dev_ios_teamid;
                        } else {
                            devData.validAPN = false;
                        }
                        devData.validAPN = true;
                        if (result.ios_cert3 && result.ios_cert3 != "") {
                            prodData.certFileName = path.join(__dirname, "../../../frontend/express/public/appcertificates/" + app_id + "/" + result.ios_cert3);
                        } else {
                            prodData.validAPN = false;
                        }
                        if (result.ios_cert_pem && result.ios_cert_pem != "") {
                            prodData.pemCertFileName = path.join(__dirname, "../../../frontend/express/public/appcertificates/" + app_id + "/" + result.ios_cert_pem);
                        } else {
                            prodData.validAPN = false;
                        }
                        if (result.ios_cert_key && result.ios_cert_key != "") {
                            prodData.pemCertKeyName = path.join(__dirname, "../../../frontend/express/public/appcertificates/" + app_id + "/" + result.ios_cert_key);
                        } else {
                            prodData.validAPN = false;
                        }
                        if (result.ios_keyid && result.ios_keyid != "") {
                            prodData.ios_keyid = result.ios_keyid;
                        } else {
                            prodData.validAPN = false;
                        }
                        if (result.ios_teamid && result.ios_teamid != "") {
                            prodData.ios_teamid = result.ios_teamid;
                        } else {
                            prodData.validAPN = false;
                        }
                        prodData.validAPN = true;

                        //Build match criteria based on audience segment.
                        let criteria = []; let geo = 0;
                        if (segmentinfo && segmentinfo.who && segmentinfo.who.length > 0) {
                            criteria = segmentinfo.who;
                        }
                        if (segmentinfo && segmentinfo.where && segmentinfo.where.length > 0) {
                            //Add only geo category items to criteria.
                            segmentinfo.where.forEach(function(item) {
                                if (item.category == 'Geo') {
                                    
                                    if (item.operand == 'gf') {
                                        geo = 1;
                                    }else{
                                        criteria.push(item);
                                    }
                                }
                            });
                        }
                        //if it is custom segment and disabled 
                    if (criteria.length == 1 && criteria[0].category == 'Custom Segment' && !semusiConfig.isCustomCsvEnabled.includes(params.qstring.app_id)) {
                        logger.info(`CustomCsv Disabled for this App ---IOS`)
                    }else{
                        let match = await common.constructMatchFromCriteria(criteria,params.qstring.app_id);

                        if (linkingField && params.childAppIds && params.childAppIds.length > 0) {
                            match[linkingField] = {
                                '$in': params.childAppIds
                            };
                        }

                        // add did if exists into param
                        if (isDid) {
                            match.did = isDid;
                        }
                        let criteriaMatch = {
                            $match: match
                        };

                        let options = {
                            "cert": "",
                            "keyid": "",
                            "teamid": "",
                            "batchFeedback": true,
                            "interval": 10,
                            "pkg": "",
                            "production": "",
                            "expiry": ""
                        };
                        logger.info(`Is Current App Mode Production =====> ${result.isappmodeproduction}======`)
                        if (result.isappmodeproduction && prodData.validAPN) {
                            if (fs.existsSync(prodData.certFileName)) {
                                options.cert = prodData.certFileName;
                                options.pemFileName = prodData.pemCertFileName;
                                options.pemKeyFileName = prodData.pemCertKeyName;
                                options.keyid = prodData.ios_keyid;
                                options.teamid = prodData.ios_teamid;
                                options.ios_passphrase = result.push_notification.apns.prod.ios_passphrase;         
                                options.pkg = result.ios_topic;
                                options.ios_auth_type = result.ios_key_type;   // auth type p8 or p12
                                options.topic = result.ios_topic ? result.ios_topic : result.push_notification.apns.prod.ios_bundleid;  //for IOS Push 
                                options.production = result.isappmodeproduction;
                                //Check if we can send a direct push to IOS users.
                                if (activeCampaign.ed != '' && activeCampaign.ed != undefined) {
                                    options.expiry = parseInt(activeCampaign.ed) - Date.now();
                                    if (options.expiry < 0) {
                                        options.expiry = semusiConfig.iOSPushExpireTime;
                                    }
                                } else {
                                    options.expiry = semusiConfig.iOSPushExpireTime;
                                }
                                let data = {
                                    campid: (activeCampaign && activeCampaign._id) ? activeCampaign._id.toString() : "",
                                    appid: app_id
                                }
                                logger.info("activeCampaign =>", activeCampaign);
                                // Removed Check for Active Campaign only, Flow will move for Draft Also : case of IOS 
                                if (activeCampaign && (activeCampaign.t == "PUSH" || activeCampaign.t == "IN-APP") && isStaticAudience(segmentinfo)) {
                                    logger.info("Active Campaign =>", app_id)
                                    logger.info("Active template =>", activeCampaign.tid)
                                    common.db.collection("templates_" + app_id).findOne({
                                        _id: common.db.ObjectID(activeCampaign.tid)
                                    }, function(err, tool) {
                                        tool.template._id = tool._id;
                                        data.content = tool.template.notificationDescription;
                                        data.toolData = {};
                                        populatePushObject(tool.template, data.toolData, params);
                                        sendDirectPushToIosUsers(criteriaMatch, options, app_id, data, activeCampaign._id, cid, geo, activeCampaign);
                                    });
                                } else {
                                    if ((activeCampaign.t == "PUSH" || activeCampaign.t == "IN-APP") ) {
                                        logger.info("Active Campaign =>", app_id)
                                        logger.info("Active template =>", activeCampaign.tid)
                                        common.db.collection("templates_" + app_id).findOne({
                                            _id: common.db.ObjectID(activeCampaign.tid)
                                        }, function(err, tool) {
                                            tool.template._id = tool._id;
                                            data.content = tool.template.notificationDescription;
                                            data.toolData = {};
                                            populatePushObject(tool.template, data.toolData, params);
                                            sendDirectPushToIosUsers(criteriaMatch, options, app_id, data, activeCampaign._id, cid, geo, activeCampaign);
                                        });
                                    }
                                }
                            }
                        } else if ((result.isappmodeproduction === undefined || result.isappmodeproduction == false) && devData.validAPN) {
                            if (fs.existsSync(devData.certFileName)) {
                                options.cert = devData.certFileName;
                                options.pemFileName = devData.pemCertFileName;
                                options.pemKeyFileName = devData.pemCertKeyName;
                                options.keyid = devData.dev_ios_keyid;
                                options.teamid = devData.dev_ios_teamid;
                                options.ios_passphrase = result.push_notification.apns.dev.dev_ios_passphrase;         
                                options.topic = result.dev_ios_topic ? result.dev_ios_topic : result.push_notification.apns.dev.dev_ios_bundleid; //for IOS Push 
                                options.ios_auth_type = result.dev_ios_key_type;   // auth type p8 or p12
                                options.pkg = result.dev_ios_topic;
                                options.production = result.isappmodeproduction;
                                //Check if we can send a direct push to IOS users.
                                if (activeCampaign.ed != '' && activeCampaign.ed != undefined) {
                                    options.expiry = parseInt(activeCampaign.ed) - Date.now();
                                    if (options.expiry < 0) {
                                        options.expiry = semusiConfig.iOSPushExpireTime;
                                    }
                                } else {
                                    options.expiry = semusiConfig.iOSPushExpireTime;
                                }
                                let data = {
                                    campid: (activeCampaign && activeCampaign._id) ? activeCampaign._id.toString() : "",
                                    appid: app_id
                                }
                                // Removed Check for Active Campaign only, Flow will move for Draft Also
                                if ((activeCampaign.t == "PUSH" || activeCampaign.t == "IN-APP") && isStaticAudience(segmentinfo)) {
                                    common.db.collection("templates_" + app_id).findOne({
                                        _id: common.db.ObjectID(activeCampaign.tid)
                                    }, function(err, tool) {
                                        tool.template._id = tool._id;
                                        data.content = tool.template.notificationDescription;
                                        data.toolData = {};
                                        populatePushObject(tool.template, data.toolData, params);
                                        sendDirectPushToIosUsers(criteriaMatch, options, app_id, data, activeCampaign._id, cid, geo, activeCampaign);
                                    });
                                } else {
                                    if ((activeCampaign.t == "PUSH" || activeCampaign.t == "IN-APP") ) {
                                        common.db.collection("templates_" + app_id).findOne({
                                            _id: common.db.ObjectID(activeCampaign.tid)
                                        }, function(err, tool) {
                                            tool.template._id = tool._id;
                                            data.content = tool.template.notificationDescription;
                                            data.toolData = {};
                                            populatePushObject(tool.template, data.toolData, params);
                                            sendDirectPushToIosUsers(criteriaMatch, options, app_id, data, activeCampaign._id, cid, geo, activeCampaign);
                                        });
                                    }
                                    }
                                }
                            }
                        }
                    } else {
                        logger.info('certificates not found');
                    }
                });

                let isjsonFileExists = false;
                const certField = result.isappmodeproduction === true ? result.android_cert3 : result.dev_android_cert3;
                   if (certField && certField != "") {
                    prodData.jsonFile = path.join(__dirname,  "../../../frontend/express/public/appcertificates/" + app_id + "/" +  certField);
                    prodData.validjsonFile = true;
                    isjsonFileExists = true;
                } else {
                    prodData.validjsonFile = false;
                }


                 if (certField && certField != "") {
                    devData.jsonFile = path.join(__dirname,  "../../../frontend/express/public/appcertificates/" + app_id + "/" + certField);
                    devData.validjsonFile = true;
                    isjsonFileExists = true;
                } else {
                    devData.validjsonFile = false;
                }


                //defining criteria so that it can be accesible in entire Funciton
                    let criteria = []; let geo = 0;
                    if (segmentinfo && segmentinfo.who && segmentinfo.who.length > 0) {
                        criteria = segmentinfo.who;
                    }
                    //what condition remove this is replcacing criteria with what 
                    if (segmentinfo && segmentinfo.where && segmentinfo.where.length > 0) {
                        logger.info("segmentinfo.where geo =>", geo);
                        //Add only geo category items to criteria.
                        segmentinfo.where.forEach(function(item) {
                            if (item.category == 'Geo') {

                                if (item.operand == 'gf') {
                                    geo = 1;
                                    logger.info("segmentinfo.where geo 2 =>", geo);
                                }else{
                                    criteria.push(item);
                                }
                            }
                        });
                    }
                if (isjsonFileExists) {
                    // handling Custom segment Section for who
                    let criteriaLength = criteria.length;
                    if (criteriaLength == 1 && criteria[0].category == 'Custom Segment') {
                        // create criteriaMatch for custom seg
                        //check if it is enabled 
                      if(semusiConfig.isCustomCsvEnabled.includes(params.qstring.app_id)){
                        let criteriaMatch = await common.constructMatchFromCriteria(criteria,params.qstring.app_id);
                                    logger.info(`Criteria Match ======> ${criteriaMatch}`)
                                  //sending push
                                    if (result.isappmodeproduction && result.isappmodeproduction == true && prodData.validjsonFile && setActive == true) {
                                        logger.info("Push Data Notification Initiated Case Custom Segment Mode Production")
                                        sendPushToAndroidUsers(criteriaMatch, prodData, app_id, pushCommand, payload, setActive, cid);
                                    } else if ((result.isappmodeproduction == undefined || result.isappmodeproduction == false) && devData.validjsonFile && setActive == true) {
                                        logger.info("Push Data Notification Initiated Case Custom Segment Mode Dev")
                                        sendPushToAndroidUsers(criteriaMatch, devData, app_id, pushCommand, payload, setActive, cid);
                                    }
                      }else{
                        logger.info(`Disabled Custom csv Push for this App - Android`)
                     }
                    } else {
                        let criteriaMatch = await common.constructMatchFromCriteria(criteria);
                        /* geo fance slient push code  */
                        // removed Check for Active Campaign only, Flow will move for Draft Also : case of Androiid 
                        if (result.isappmodeproduction && result.isappmodeproduction == true && prodData.validjsonFile) {
                            logger.info("segmentinfo.where geo 3 =>", geo);
                            sendPushToAndroidUsers(criteriaMatch, prodData, app_id, pushCommand, payload, setActive, cid, geo, activeCampaign);
                        } else if ((result.isappmodeproduction == undefined || result.isappmodeproduction == false) && devData.validjsonFile ) {
                            logger.info("segmentinfo.where geo 4 =>", geo);
                            sendPushToAndroidUsers(criteriaMatch, devData, app_id, pushCommand, payload, setActive, cid, geo, activeCampaign);
                        }
                        if (activeCampaign && geo == undefined) {
                            if (result.isappmodeproduction && result.isappmodeproduction == true && prodData.validjsonFile ) {
                                sendPushToAndroidUsers(criteriaMatch, prodData, app_id, pushCommand, payload, setActive, cid);
                            } else if ((result.isappmodeproduction == undefined || result.isappmodeproduction == false) && devData.validjsonFile) {
                                sendPushToAndroidUsers(criteriaMatch, devData, app_id, pushCommand, payload, setActive, cid);
                            }
                        }
                    }
                    
                }
                // send web push
                let config = (result.isappmodeproduction) ? (result.webPush_prod) ? result.webPush_prod : {} : (result.webPush_dev) ? result.webPush_dev : {};
                if (setActive && config && Object.keys(config).length > 0) {
                    let match = await prepareQuery(segmentinfo, params, linkingField, isDid);
                  if (criteria.length == 1 && criteria[0].category == 'Custom Segment' && !semusiConfig.isCustomCsvEnabled.includes(params.qstring.app_id)) {
                    logger.info(`Disabled Custom csv Push for this App - Web`)
                 }else{

                    campaignUtils.sendWebPush(match, config, app_id, payload,segmentinfo);
                  }
                }
            }
        });
    };



    let prepareQuery = async function(segmentinfo, params, linkingField, isDid) {
        let criteria = [];
        if (segmentinfo && segmentinfo.who && segmentinfo.who.length > 0) {
            criteria = segmentinfo.who;
        }
        if (segmentinfo && segmentinfo.where && segmentinfo.where.length > 0) {
            //Add only geo category items to criteria.
            segmentinfo.where.forEach(function(item) {
                if (item.category == 'Geo') {
                    criteria.push(item);
                }
            });
        }
        let match = await common.constructMatchFromCriteria(criteria);
        if (linkingField && params.childAppIds && params.childAppIds.length > 0) {
            match[linkingField] = {
                '$in': params.childAppIds
            };
        }

        // add did if exists into param
        if (isDid) {
            match.did = isDid;
        }

        match.active = true;
        return match;
    }


    /**
    * Processes and enriches a campaign object with default values and calculated metrics.
    * This function initializes default values for various campaign properties, calculates metrics such as CTR and impression rate,
    * and retrieves additional campaign data based on the provided campaign ID.
    * @param {string} cid - The campaign ID used to retrieve additional campaign data if valid.
    * @param {Object} campaign - The campaign object to be processed and enriched.
    * @returns {Promise<Object>} - The enriched campaign object with default values and calculated metrics.
    */
    async function prepareCampaignDetails(cid, campaign) {
        if (!campaign) return;

        // Default values
        campaign.pushedTo = campaign.Campaign_PushedTo || 0;
        campaign.reach = campaign.Campaign_Reach || 0;
        campaign.clickedTo = campaign.Campaign_ClickedTo || 0;
        campaign.receivedTo = campaign.Campaign_ReceivedTo || 0;
        campaign.conversion = campaign.conversion || 0;
        campaign.aud = campaign.aud || common.db.ObjectID();
        campaign.c_maker = campaign.c_maker || "NA";
        campaign.c_checker = campaign.c_checker || "NA";
        campaign.inbox_read = campaign.inbox_read || 0;
        campaign.Inbox_clicked = campaign.Inbox_clicked || 0; // Assuming Inbox_clicked is intended

        // Calculate CTR
        if (campaign.clickedTo && campaign[semusiConfig.campaignColumn]) {
            campaign.ctr = `${parseFloat((campaign.clickedTo / campaign[semusiConfig.campaignColumn]) * 100).toFixed(2)}%`;
        } else {
            campaign.ctr = "0%";
        }

        // Calculate Impression Rate
        if (campaign.pushedTo) {
            campaign.impressionRate = `${parseFloat((campaign.receivedTo / campaign.pushedTo) * 100).toFixed(2)}%`;
        } else {
            campaign.impressionRate = "0%";
        }

        // Retrieve additional data if cid is valid
        if (cid.length === 24) {
            try {
                const camp = await campaignDb.retrieveCampData(cid);
                campaign.appinbox_et = camp.appinbox_et ? moment(parseInt(camp.appinbox_et)) : "";
            } catch (error) {
                console.error(`Error retrieving campaign data for CID ${cid}:`, error);
            }
        }

        return campaign;
    }

    /**
     * Formats and enriches campaign data for pagination and output.
     * This function processes the campaign data, calculates additional metrics such as CTR and impression rate,
     * and formats it for inclusion in the response object.
     * 
     * @param {Object} params - The parameters for the pagination request, typically containing metadata and settings.
     * @param {Object} retObj - The object where the processed campaign data will be stored.
     * @param {Array} campaigns - An array of campaign objects to be processed.
     */
        function formatCampaignDataForPagination(params, retObj, campaigns) {

            // Process each campaign to prepare data for pagination
            let retVal = campaigns.map(campaign => {
                return {
                    _id: campaign._id.toString(),  // Convert campaign ID to string
                    pushedTo: campaign.Campaign_PushedTo || 0,  
                    reach: campaign.Campaign_Reach || 0,
                    clickedTo: campaign.Campaign_ClickedTo || 0, 
                    receivedTo: campaign.Campaign_ReceivedTo || 0,  
    
                    // Calculate CTR (Click Through Rate) and Impression Rate
                    ctr: (campaign.Campaign_ClickedTo && campaign.Campaign_Reach)
                        ? `${((campaign.Campaign_ClickedTo / campaign.Campaign_Reach) * 100).toFixed(2)}%`
                        : '0%',
                    impressionRate: (campaign.Campaign_ReceivedTo && campaign.Campaign_PushedTo)
                        ? `${((campaign.Campaign_ReceivedTo / campaign.Campaign_PushedTo) * 100).toFixed(2)}%`
                        : '0%',
    
                    conversion: campaign.conversion || 0,
    
                    // Additional campaign details from statsData
                    sd: campaign.sd,  
                    ed: campaign.ed,  
                    cid: campaign._id,  
                    nm: campaign.cname,
                    t: campaign.type,  
                    segmentName: campaign.segmentname,  
                    segmentinfo: campaign.segmentinfo,  
                    active: campaign.active,  
                    aud: campaign.aud || common.db.ObjectID(),  
                    variant: campaign.variant || false 
                };
            });
    
            // Assign processed data to the output object
            retObj.data = retVal;
    
            // Return the output using the common method
            common.returnOutput(params, retObj);
        }
        
        /**
         * Merges two arrays of campaigns based on their _id properties.
         * If an _id exists in both arrays, their objects are merged with the properties from the second array taking precedence.
         * 
         * @param {Array} camp_timely_data - The Array of Campaigns from Timely_data .
         * @param {Array} camp_statsdata - The Array of Campaigns from statsData taking precedence in case of conflicts.
         * @returns {Array} - camp_merged_data , An array of merged objects, where each object is unique by _id.
         */
    
        function mergeCampaignObjects(camp_timely_data, camp_statsdata) {
            // Create a lookup table from camp_timely_data
            const lookup = camp_timely_data.reduce((acc, item) => {
                acc[item._id] = { ...item }; // Copy the item to avoid mutation
                return acc;
            }, {});
    
            // Merge camp_statsdata objects into the lookup table
            camp_statsdata.forEach(item => {
                lookup[item._id] = { ...lookup[item._id], ...item }; // Merge items
            });
    
            // Convert the lookup table back to an array
            return Object.values(lookup);
        }
    
        /**
         * Aggregates data from campaigns by date and sums up values for each campaign.
         * The aggregation is done on a date-wise basis, combining data from different platforms and hours.
         * 
         * @param {Array} inputDataArray - An array of campaign data objects. Each object should have data grouped by platform and date.
         * @returns {Array} - An array of aggregated campaign data objects, where each object is summed up by campaign ID.
         */
        function aggregateCampaignDataByDate(inputDataArray, sd, ed) {
    
    
            // Turn epochs in the format of YYYY_MM_DD
            const startDate = dateFunctions.convertEpochToFormattedDate(sd)
            const endDate = dateFunctions.convertEpochToFormattedDate(ed)
    
    
            const result = {};
    
            inputDataArray.forEach((inputData) => {
                const fullId = inputData._id;
    
                // Extract Campid from _id (characters 25 to 48)
                const campId = fullId.substring(24, 48);
    
                // Initialize result entry for this Campid if it doesn't exist
                if (!result.hasOwnProperty(campId)) {
                    result[campId] = { _id: campId };
                }
    
                const platforms = ["android", "ios", "web"];
    
                platforms.forEach((platform) => {
                    if (inputData.hasOwnProperty(platform)) {
                        for (const date in inputData[platform]) {
                            if (dateFunctions.isDateInRange(date, startDate, endDate)) {
    
                                if (inputData[platform].hasOwnProperty(date)) {
                                    const dateData = inputData[platform][date];
    
                                    for (const hour in dateData) {
                                        if (dateData.hasOwnProperty(hour)) {
                                            const hourData = dateData[hour];
    
                                            for (const key in hourData) {
                                                if (hourData.hasOwnProperty(key)) {
                                                    if (result[campId].hasOwnProperty(key)) {
                                                        result[campId][key] += hourData[key];
                                                    } else {
                                                        result[campId][key] = hourData[key];
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
    
                        }
                    }
                });
            });
    
            // Convert result object to an array of objects
            return Object.values(result);
        }
    
        /**
         * Processes and aggregates campaign data on a day-wise basis within a specified date range.
         * It retrieves campaign details from the database, aggregates the data, and merges it with the original campaign list from statdata.
         * @param {Array} campaigns - An array of campaign objects to be processed.
         * @param {string} appId - The application ID associated with the campaigns.
         * @param {number} startEpoch - The start date  for the data aggregation in epoch format.
         * @param {number} endEpoch - The end date for the data aggregation in epoch format.
         */
        async function updateCampaignsDaywiseStatus(params, camp_statsdata, appId, startEpoch, endEpoch) {
    
    
            // conver the epoch into the common format 
            const sd = dateFunctions.ConvertEpochtoCommonFormatDate(startEpoch);
            const ed = dateFunctions.ConvertEpochtoCommonFormatDate(endEpoch);
    
    
            // Retrieve and make the _IDs to fetch with appid & Date 
            const campids = camp_statsdata.map(campaign => {
                return common.getCampidnAppidwithDates(appId, campaign._id.toString(), sd, ed);
            }).flat();
    
            // Retrieve detailed campaign data based on generated IDs
            const campDetails = await campaignDb.retrieveCampDataFromTimely_campaign_statsdata(campids)
    
    
            if (campDetails) {
    
                // Aggregate and process campaign data by date
                const camp_timely_data = aggregateCampaignDataByDate(campDetails, startEpoch, endEpoch)
    
                // Merge timely data with existing campaign stats
                const camp_merged_data = mergeCampaignObjects(camp_timely_data, camp_statsdata)
    
                // return the data
                return camp_merged_data 

            }
    
    }

    let sendPushToAndroidUsers = function(match, data, app_id, pushCommand, payload, setActive, cid, geo, activeCampaign) {
        payload = (payload === undefined) ? {} : payload;
        let appId = app_id;
        logger.info(`segmentinfo.where geo 5 =>${geo}`);
        let activeCollection ='campaigns_' + app_id ;   // Passing the collection Name 
        logger.info(`Match sendPushToAndroidUsers => ${JSON.stringify(match)}`)
        campaignUtils.getUsersMsgToken(match, app_id)
            .then(([fcmids, dids]) => {
                logger.info(`fcmids length in sendPushToAndroidUsers => ${fcmids.length}`)
                // If fcm id is present for this deviceId then move flow to fcm 
                if(fcmids.length > 0 ){
                   campaignUtils.sendFCMMessageWithPayload(data.jsonFile, fcmids, dids, payload, pushCommand, activeCollection, cid, appId, geo, activeCampaign);
                }
            })
            .catch(error => {
                logger.error(`Error Catched in Android => ! getUsersMsgToken Failed  => ${error}`);
                return;
            });
    };


    let sendPushToIosUsers = function(match, options, app_id, cid) {
        common.db.collection('app_users' + app_id).aggregate([
            match,
            {
                $match: {
                    "p": "IOS",
                    "active": true
                }
            },
            {
                $project: {
                    "gcmid": 1,
                    "p": 1
                }
            }
        ], function(err, deviceids) {
            let j = 0;
            let deviceArr = [];
            for (let i = 0; i < deviceids.length; i++) {
                if (j == 100 || (i + 1 == deviceids.length)) {
                    deviceArr.push(deviceids[i].gcmid);
                    sendApnPush(options, deviceArr, "#CAMPAIGNUPDATE", cid);
                    j = 0;
                    deviceArr = [];
                } else {
                    deviceArr.push(deviceids[i].gcmid);
                    j++;
                }
            }
        });
    };

    let sendApnPush = function(options, ids, content, cid) {
        ids.forEach(function(apnId) {
            try {
                let apnConnection = new apn.Connection(options);
                let myDevice = new apn.Device(apnId);
                let note = new apn.Notification();
                note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
                note.payload = {
                    'COMMAND': content,
                    aps: {
                        "content-available": true
                    }
                };
                let data = apnConnection.pushNotification(note, myDevice, cid);
            } catch (e) {
                logger.error("ERROR sending apn push." + e);
            }
        })
    };

   

    let sendDirectPushToIosUsers = function(match, options, app_id, content, campid,cid,geo, activeCampaign) {
        logger.info(`sendDirectPushToIosUsers=================campid=====${campid}===`)
        common.db.collection('app_users' + app_id).aggregate([
            match,
            {
                $match: {
                    "p": "IOS",
                    "active": true
                }
            },
            {
                $project: {
                    "id": 1,
                    "did": 1,
                    "gcmid": 1,
                    "_custom_UserID":1,
                    "_custom":1,
                    "_custom_UserId":1,
                    "p": 1,
                    history: 1,
                    _id: 0
                }
            }
        ], async function(err, deviceids) {
            let j = 0;
            let deviceArr = [];
            let custom_UserIDArr = []; let customArr = [];
            if(deviceids != undefined && deviceids != null) {
            logger.info(`deviceids======${deviceids}===========campid=====${campid}===`)
            try {    
            for (let i = 0; i < deviceids.length; i++) {
                if (j == 100 || (i + 1 == deviceids.length)) {
                    logger.info("===== Initiated Push for IOS =====",deviceids)
                    deviceArr.push(deviceids[i]);
                    
                    if(deviceids[i]._custom_UserId != undefined){
                        custom_UserIDArr = deviceids[i]._custom_UserId;
                    }
                   // fetch iOS GCM token
                    const didDetails = await campaignUtils.getUsersMsgTokenIos(deviceArr, app_id,campid);
                    logger.info(`DidDetails for ios , ${JSON.stringify(didDetails)}`)
                    logger.info(`Initiating Apn Push Notification=============> campid=====${campid}==`)
                    apnPush.pushNotification(options, didDetails, content, app_id, campid,geo, activeCampaign, custom_UserIDArr,customArr);
                    j = 0;
                    deviceArr = [];
                    custom_UserIDArr=[];
                } else {
                    deviceArr.push(deviceids[i]);
                    if(deviceids[i]._custom_UserID != undefined){
                        custom_UserIDArr.push(deviceids[i]._custom_UserID);
                    }
                    if(deviceids[i]._custom != undefined){
                        customArr.push(deviceids[i]._custom);
                    }
                    j++;
                }
            }
        } catch (error) {
            logger.info('error in sendDirectPushToIosUsers =>', error)       
        }
        }else{
            logger.info(`deviceids null or Undefined======${deviceids}===========campid=====${campid}===`)
        }
        });
    };

    isStaticAudience = function(segmentinfo) {
        let retVal = true;
        if (segmentinfo) {
            if (segmentinfo.where) {
                for (let i = 0; i < segmentinfo.where.length; i++) {
                    let item = segmentinfo.where[i];
                    if (item.category == "Geo") {
                        retVal = true;
                        break;
                    } else {
                        retVal = false;
                    }
                }
            }
            if (segmentinfo.what && segmentinfo.what.length > 0) {
                retVal = false;
            }
            if (segmentinfo.when && segmentinfo.when.length > 0) {
                retVal = false;
            }
        }
        return retVal;

    }
    /**
     * Support for existing format of time range i.e. 8, 12, etc to be converted into 0800, 1200 etc.
     * Converts a given string into a 24-hour format time string. 
     * @param {string | number} inputStr - The input string representing the hour
     * @returns {string} - The time string in 24-hour format (HHMM).
     */
    function convertToTime(inputInt) {
        // Convert integer to string and pad with leading zeros if necessary
        const inputStr = String(inputInt).padStart(4, '0');
        
        // Extract the hour and minute parts
        const hour = inputStr.slice(0, 2);
        const minutes = inputStr.slice(2);
        // Return the formatted time string
        return `${hour}${minutes}`;
    }


    let getCampaignsStats = function(result) {
        let dataObj = {
            'android': {},
            'ios': {}
        };
        let platforms = ['android', 'ios'];
        let cityList = [];
        let timeList = [];
        let stats = {
            "Campaign_Received": 0,
            "Campaign_Viewed": 0,
            "Campaign_Clicked": 0,
            "Campaign_Deleted": 0
        }

        result.forEach(function(data) {
            platforms.forEach(function(p) {
                // check platform into data
                if (data[p]) {
                    // get dates list
                    let dates = Object.keys(data[p]);
                    dates.forEach(function(date) {
                        if (dataObj[p][date] == undefined) {
                            dataObj[p][date] = {}
                        }
                        // get hrs list
                        let hours = Object.keys(data[p][date]);
                        hours.forEach(function(hrs) {
                            timeList.push(moment(date + ' ' + hrs, 'YYYY_MM_DD HH').valueOf() / 1000);
                            // get devices list
                            let devices = Object.keys(data[p][date][hrs]);
                            devices.forEach(function(device) {
                                if (dataObj[p][date][device] == undefined) {
                                    dataObj[p][date][device] = {}
                                }
                                // get country list
                                let countries = Object.keys(data[p][date][hrs][device]);
                                countries.forEach(function(country) {
                                    // get city list
                                    let cities = Object.keys(data[p][date][hrs][device][country]);
                                    cities.forEach(function(city) {
                                        if (dataObj[p][date][device][city] == undefined) {
                                            dataObj[p][date][device][city] = {
                                                "Campaign_Received": 0,
                                                "Campaign_Viewed": 0,
                                                "Campaign_Clicked": 0,
                                                "Campaign_Deleted": 0
                                            }
                                        }
                                        // add city into list
                                        cityList.push({
                                            name: city,
                                            number: 1
                                        });
                                        // get locations list
                                        let locations = Object.keys(data[p][date][hrs][device][country][city]);
                                        locations.forEach(function(location) {
                                            // check campaign receive
                                            if (data[p][date][hrs][device][country][city][location]['Campaign_Received']) {
                                                dataObj[p][date][device][city]['Campaign_Received'] += data[p][date][hrs][device][country][city][location]['Campaign_Received'];
                                                stats['Campaign_Received'] += data[p][date][hrs][device][country][city][location]['Campaign_Received'];
                                            }
                                            // check campaign viewed
                                            if (data[p][date][hrs][device][country][city][location]['Campaign_Viewed']) {
                                                dataObj[p][date][device][city]['Campaign_Viewed'] += data[p][date][hrs][device][country][city][location]['Campaign_Viewed'];
                                                stats['Campaign_Viewed'] += data[p][date][hrs][device][country][city][location]['Campaign_Viewed'];
                                            }
                                            // check campaign clicked
                                            if (data[p][date][hrs][device][country][city][location]['Campaign_Clicked']) {
                                                dataObj[p][date][device][city]['Campaign_Clicked'] += data[p][date][hrs][device][country][city][location]['Campaign_Clicked'];
                                                stats['Campaign_Clicked'] += data[p][date][hrs][device][country][city][location]['Campaign_Clicked'];
                                            }
                                            // check campaign delete
                                            if (data[p][date][hrs][device][country][city][location]['Campaign_Deleted']) {
                                                dataObj[p][date][device][city]['Campaign_Deleted'] += data[p][date][hrs][device][country][city][location]['Campaign_Deleted'];
                                                stats['Campaign_Deleted'] += data[p][date][hrs][device][country][city][location]['Campaign_Deleted'];
                                            }
                                        });
                                    });
                                });
                            });
                        });
                    });
                }
            });
        });

        // get best and wrost city
        if (cityList) {
            let newList = _.countBy(cityList, function(item) {
                return item.name;
            });
            dataObj.bestCity = _(newList).chain().pairs().max(function(p) {
                return p[1];
            }).value()[0];
            dataObj.wrostCity = _(newList).chain().pairs().min(function(p) {
                return p[1];
            }).value()[0];
        }

        // get best and wrost time
        if (timeList) {
            let newTimeList = _.groupBy(timeList);
            let keys = _.keys(newTimeList);
            dataObj.bestTime = keys[0];
            dataObj.wrostTime = keys.pop();
        }

        return {
            objStats: dataObj,
            stats: stats
        };
    }

    let sendPushyCampaign = function(app_id, access_token, camp) {
        let campaigns = {}

    }

    templateApi.getCampaignsImg = function(params) {
        if (params.qstring.campImg) {
            setTimeout(function() {
                params.res.writeHead(301, {
                    Location: params.qstring.campImg
                });
                params.res.end();
            }, 3000);
        } else {
            common.returnOutput(params, '');
        }
    }

    //save varients for selected app
    templateApi.saveVariant = function(params) {
        let argProps = {
                'name': {
                    'required': true,
                    'type': 'String'
                },
                'operand': {
                    'required': true,
                    'type': 'String'
                },
                'operator': {
                    'required': true,
                    'type': 'String'
                }
            },
            newVariant = {};
        
        if (!(newVariant = common.validateArgs(JSON.parse(params.qstring.args), argProps))) {
            common.returnMessage(params, 400, 'Not enough args');
            return false;
        }
        newVariant['value'] = params.qstring.args.value;
        newVariant['app_id'] = params.qstring.app_id;
        newVariant['createdAt'] = common.getCurrentEpochTime();
        common.db.collection('template_variants').insert(newVariant, function(err, tmpl) {
            if (!err && tmpl.ops) {
                newVariant._id = tmpl.ops[0]._id;
            }
            common.returnOutput(params, newVariant);
        });
    };
    //get varients for selected app 
    templateApi.getTemplateMetaData = function (params) {
        if (!(params.qstring.app_id)) {
            logger.error('Missing app_id in params.qstring');
            common.returnMessage(params, 400, 'Not enough args');
            return false;
        }
        
        common.db.collection('apps').find({ '_id': common.db.ObjectID(params.qstring.app_id) }).toArray(function (err, data) {
            if (err || !data || data.length === 0) {
                logger.error(`Error finding app with id ${params.qstring.app_id}: ${err ? err.message : 'No data found'}`);
                common.returnMessage(params, 500, 'Something went wrong');
            } else {
                const app = data[0];
                let allowedListArr = [];
    
                if (app.allowedItems && app.allowedItems.length > 0) {
                    let allowedListStr = ''; // set the default src for all apps
    
                    app.allowedItems.forEach(function (url) {
                        // checking if the user entered is a valid URL
                        if (common.isValidUrl(url)) {
                            // Then generate its baseurl 
                            allowedListArr.push(new URL(url).origin);
                        } else {
                            // otherwise push in the array 
                            allowedListArr.push(url);
                        }
                        allowedListStr += ' ' + url;
                    });
    
                    // Update CSP policy
                    semusiConfig.appListSrc = allowedListStr;
                    logger.info('CSP policy updated with allowed URLs');
                } else {
                    logger.error(`No allowedItems found for app id ${params.qstring.app_id}`);
                }
    
                common.db.collection('template_variants').find({ 'app_id': params.qstring.app_id }).toArray(function (err, varaints) {
                    if (err) {
                        logger.error('Error retrieving template variants: ' + JSON.stringify(err));
                        common.returnOutput(params, err);
                    } else {
                        // Helper function to get the origin or return the URL as is
                        const getUrlOriginOrAsIs = (url) => {
                            if (common.isValidUrl(url)) {
                                return new URL(url).origin;  // Return the origin if valid
                            } else {
                                return url;  // Return the URL as is if invalid
                            }
                        };
                        let data = {
                            allowedUrlList: allowedListArr,
                            blobUrls :[
                                getUrlOriginOrAsIs(semusiConfig.blobContainerCDN),
                                getUrlOriginOrAsIs(semusiConfig.azureCDNBlobSASURL),
                                getUrlOriginOrAsIs(semusiConfig.e2eContainerCDN)
                            ]
                        };
    
                        if (varaints) {
                            data.varaints = varaints;
                            logger.info(`Found ${varaints.length} template variants for app id ${params.qstring.app_id}`);
                            common.returnOutput(params, data);
                        } else {
                            logger.error(`No template variants found for app id ${params.qstring.app_id}`);
                            common.returnOutput(params, data);
                        }
                    }
                });
            }
        });
    };
    
}(templateApi));

module.exports = templateApi;
