var express = require('express');
var router = express.Router();
var Project = require("../models/project");
var winston = require('../config/winston');
var Project_user = require("../models/project_user");
var operatingHoursService = require("../services/operatingHoursService");
var AnalyticResult = require("../models/analyticResult");
var Department = require("../models/department");
var RoleConstants = require("../models/roleConstants");
var cacheUtil = require('../utils/cacheUtil');
var cacheEnabler = require("../services/cacheEnabler");
var pubModulesManager = require('../pubmodules/pubModulesManager');  // on constructor init is undefined beacusae pub module is loaded after
// console.log("pubModulesManager.cache", pubModulesManager.cache);
const Faq_kb = require("../models/faq_kb");

router.get('/load', function(req, res, next) {

  winston.debug(req.projectid);
  
  // https://stackoverflow.com/questions/24258782/node-express-4-middleware-after-routes
  next();      // <=== call next for following middleware 
  // redirect to widget
});

router.get('/', async (req, res, next) => {

    winston.debug(req.projectid);


    var getIp = function() {
      return new Promise(function (resolve, reject) {
        // console.log("getIp")
      //  var ip = req.ip;
      var ip = req.headers['x-forwarded-for'] || 
       req.connection.remoteAddress || 
       req.socket.remoteAddress ||
       (req.connection.socket ? req.connection.socket.remoteAddress : null);
        winston.debug("ip:"+ ip);
        return resolve(ip);
      });
    };


    
    // console.log("pubModulesManager.cache",pubModulesManager.cache);
    // console.log("cacheClient",cacheClient);
    let cacheClient = undefined;
    if (pubModulesManager.cache) {
      cacheClient = pubModulesManager.cache._cache._cache;  //_cache._cache to jump directly to redis modules without cacheoose wrapper (don't support await)
    }

    var cacheKey = req.projectid+":widgets";
    
    if (cacheEnabler.widgets && cacheClient) {
      winston.debug("Getting cache for widgets key: " + cacheKey);       
      const value = await cacheClient.get(cacheKey);

      if (value) {
        winston.debug("Getted cache for widgets key: " + cacheKey + " value:", value);   

        value.ip = await getIp(); //calculate ip each time without getting it from cache 
        
        winston.debug("value",value);

        res.json(value);
        // https://stackoverflow.com/questions/24258782/node-express-4-middleware-after-routes
        next();      // <=== call next for following middleware 
        return;
      } else {
          winston.debug("Widget cache NOT found");
      }
      
    }
    // console.log("/widgets without cache found");

    var availableUsers = function() {
      winston.debug('availableUsers:');
      return new Promise(function (resolve, reject) {
        if (!req.project) {
          return reject({err: "Project Not Found"});
        }
      operatingHoursService.projectIsOpenNow(req.projectid, function (isOpen, err) {    
          winston.debug('isOpen:'+ isOpen);
          if (isOpen) {            
            // rolequery
            Project_user.find({ id_project: req.projectid, user_available: true, roleType: RoleConstants.TYPE_AGENTS, status: "active" }).
            // Project_user.find({ id_project: req.projectid, user_available: true, role: { $in : [RoleConstants.OWNER, RoleConstants.ADMIN, RoleConstants.SUPERVISOR, RoleConstants.AGENT]}, status: "active" }).
              populate('id_user').
              exec(function (err, project_users) {
                winston.debug('project_users:'+ project_users);
                if (project_users) {    
                  user_available_array = [];
                  project_users.forEach(project_user => {
                    if (project_user.id_user) {
                      user_available_array.push({ "id": project_user.id_user._id, "firstname": project_user.id_user.firstname });
                    } else {
                      winston.debug('else:');
                    }
                  });      
                  winston.debug('user_available_array:'+ JSON.stringify(user_available_array));          
                  return resolve(user_available_array);
                }
              });
          } else {          
            return resolve([]);
          }
        });
      });
  };

  var waiting = function() {
    return new Promise(function (resolve, reject) {
      AnalyticResult.aggregate([
        //last 4
        { $match: {"id_project":req.projectid, "createdAt" : { $gte : new Date((new Date().getTime() - (4 * 60 * 60 * 1000))) }} },
        { "$group": { 
          "_id": "$id_project", 
          "waiting_time_avg":{"$avg": "$waiting_time"}
        }
      },
      
    ])
    // .cache(cacheUtil.longTTL, req.projectid+":analytics:query:waiting:avg:4hours")        
    .exec(function(err, result) {
          return resolve(result);
    });
  });
  };

  

  var botsRules = function() {
    return new Promise(function (resolve, reject) {
      Faq_kb.find({ "id_project": req.projectid, "trashed": { $in: [null, false] } }, function (err, bots) {
        winston.debug("bots",bots);
        let rules = [];
        bots.forEach(function(bot) { 
          winston.debug("bot.attributes",bot.attributes);
          // && bot.attributes.rules.length > 0
          if (bot.attributes && bot.attributes.rules) {
            winston.debug("bot.attributes.rules",bot.attributes.rules);
            bot.attributes.rules.forEach(function(rule) {
              rules.push(rule);
            });
            // rules.concat(bot.attributes.rules);
          }
        });
        winston.debug("resolve",rules);
        // return resolve(bots);
        return resolve(rules);
        
      });
    });
  }

  
  var getDepartments = function(req) {

    return new Promise(function (resolve, reject) {

      var query = { "id_project": req.projectid, "status": 1 };

      winston.debug("req.project:" + JSON.stringify(req.project));
      
      if (req.project) {
                                    //secondo me qui manca un parentesi tonda per gli or
        if (req.project.profile && (req.project.profile.type === 'free' && req.project.trialExpired === true) || (req.project.profile.type === 'payment' && req.project.isActiveSubscription === false)) {
          query.default = true;
        }
  
        winston.debug("query:", query);
  
        Department.find(query).exec(function(err, result) {
              return resolve(result);
        });
      } else {        
          return reject({err: "Project Not Found"});        
      }


    });

  }


  var getProject = function(req) {
    winston.debug('getProject.');

    return new Promise(function (resolve, reject) {

       //@DISABLED_CACHE .cache(cacheUtil.queryTTL, "projects:query:id:status:100:"+req.projectid+":select:-settings")            
      
      Project.findOne({_id: req.projectid, status: 100}).select('-ipFilter -ipFilterEnabled').exec(function(err, project) {
        // not use .lean I need project.trialExpired 

          if (err) {
            return reject({err: "Project Not Found"});        
          }


          winston.debug("project", project);

          // This code filters the project settings to only include the properties
          // 'allowed_urls', 'allowed_urls_list', 'allowed_send_emoji' and 'allowed_upload_extentions' removing all others.
          // Removed the "-settings" command from the query that excluded the entire "settings" field from the selection
          if (project && project.settings) {
            const { allowed_urls, allowed_urls_list, allow_send_emoji, allowed_upload_extentions } = project.settings;
            project.settings = {};
            Object.assign(project.settings, 
              allowed_urls !== undefined ? { allowed_urls } : {},
              allowed_urls_list !== undefined ? { allowed_urls_list } : {},
              allow_send_emoji !== undefined ? { allow_send_emoji } : {},
              allowed_upload_extentions !== undefined ? { allowed_upload_extentions } : {}
            );
          }

          // ProjectSetter project not found with id: 62d8cf8b2b10b30013bb9b99
          // Informazioni
          // 2022-07-27 14:32:14.772 CESTerror: Error getting widget. {"err":"Project Not Found"}
          // Informazioni
          // 2022-07-27 14:32:14.778 CESTerror: uncaughtException: Cannot read property 'profile' of null
          // Informazioni
          // 2022-07-27 14:32:14.778 CESTTypeError: Cannot read property 'profile' of null at /usr/src/app/routes/widget.js:132:124 at /usr/src/app/node_modules/mongoose/lib/model.js:5074:18 at processTicksAndRejections (internal/process/task_queues.js:79:11) {"date":"Wed Jul 27 2022 12:32:14 GMT+0000 (Coordinated Universal Time)","error":{},"exception":true,"os":{"loadavg":[0.26,0.51,0.58],"uptime":1028128},"process":{"argv":["/usr/local/bin/node","/usr/src/app/bin/www"],"cwd":"/usr/src/app","execPath":"/usr/local/bin/node","gid":0,"memoryUsage":{"arrayBuffers":128833077,"external":130521753,"heapTotal":110641152,"heapUsed":85605912,"rss":310054912},"pid":26,"uid":0,"version":"v12.22.12"},"stack":"TypeError: Cannot read property 'profile' of null\n at /usr/src/app/routes/widget.js:132:124\n at /usr/src/app/node_modules/mongoose/lib/model.js:5074:18\n at processTicksAndRejections (internal/process/task_queues.js:79:11)","trace":[{"column":124,"file":"/usr/src/app/routes/widget.js","function":null,"line":132,"method":null,"native":false},{"column":18,"file":"/usr/src/app/node_modules/mongoose/lib/model.js","function":null,"line":5074,"method":null,"native":false},{"column":11,"file":"internal/process/task_queues.js","function":"processTicksAndRejections","line":79,"method":null,"native":false}]}

          // console.log("project!=null",project!=null);
          // console.log("project.profile",project.profile);
                                              //secondo me qui manca un parentesi tonda per gli or
          if (project  && project.profile && ((project.profile.type === 'free' && project.trialExpired === true) || (project.profile.type === 'payment' && project.isActiveSubscription === false))) {
            winston.debug('getProject remove poweredBy tag', project);

            if (project.widget) {
              project.widget.poweredBy = undefined;
              project.widget.baloonImage = undefined;            
            }
            
          }

        return resolve(project);
      });        

    });

  }

// TOOD add labels
    Promise.all([

        getProject(req)
      ,
        availableUsers()
      ,
        getDepartments(req)
      // ,
        // waiting()unused used 620e87f02e7fda00350ea5a5/publicanalytics/waiting/current
      , 
        getIp()
      ,
        botsRules()
     

    ]).then(function(all) {
      // console.log("all", all);
      let result = {project: all[0], user_available: all[1], departments: all[2], ip: all[3], botsRules: all[4]};
      // let result = {project: all[0], user_available: all[1], departments: all[2], waiting: all[3], ip: all[4]};

      
      if (cacheEnabler.widgets && cacheClient) {   
        let cloned_result = Object.assign({}, result);     
        delete cloned_result.ip; //removing uncachable ip from cache
        winston.debug("Creating cache for widgets key: " + cacheKey);       
        cacheClient.set(cacheKey, cloned_result, cacheUtil.longTTL, (err, reply) => {
            winston.verbose("Created cache for widgets",{err:err});
            winston.debug("Created cache for widgets reply:"+reply);
        });

      }
      
      res.json(result);
      // https://stackoverflow.com/questions/24258782/node-express-4-middleware-after-routes
      next();      // <=== call next for following middleware 

    }).catch(function(err) {
      winston.error('Error getting widget.', err);
      return res.status(500).send({success: false, msg: 'Error getting widget.'});
    });


  });



  


  router.get('/ip', function(req, res, next) {
  
     var xforwarded = req.headers['x-forwarded-for'];
     winston.info('xforwarded'+ xforwarded);

     var connectionRemoteAddress  = req.connection.remoteAddress;
     winston.info('connectionRemoteAddress'+ connectionRemoteAddress);

     var socketRemoteAddress = req.socket.remoteAddress;
     winston.info('socketRemoteAddress'+ socketRemoteAddress);

    if (req.connection.socket ) {
      var connectionSocketRemoteAddress = req.connection.socket.remoteAddress;
      winston.info('connectionSocketRemoteAddress'+ connectionSocketRemoteAddress);

    }
    

    var ip = req.headers['x-forwarded-for'] || 
     req.connection.remoteAddress || 
     req.socket.remoteAddress ||
     (req.connection.socket ? req.connection.socket.remoteAddress : null);
    winston.info("ip:"+ ip);




    const ipStandard = (req.headers['x-forwarded-for'] || '').split(',').shift().trim() ||        //https://stackoverflow.com/questions/8107856/how-to-determine-a-users-ip-address-in-node
    req.socket.remoteAddress

    winston.info("standard ip: "+ipStandard); // ip address of the user



    // const parseIp = (req) =>
    // req.headers['x-forwarded-for']?.split(',').shift()
    // || req.socket?.remoteAddress

    

    let parseIp = req.socket.remoteAddress;

    const xFor =  req.headers['x-forwarded-for'];
    winston.info("parseIp xFor: "+xFor);

    if (xFor ) {
      const xForArr = xFor.split(',');
      if (xForArr && xForArr.length>0) {
        parseIp = xForArr.shift();
        winston.info("parseIp xFor parseIp: "+parseIp);
      }
    }
    winston.info("parseIp: "+parseIp); // ip address of the user


    res.json( {ip:ip, ipStandard:ipStandard, parseIp: parseIp} );


  });

  


module.exports = router;
