const request = require('request');
const mongoose = require("mongoose");
const passport = require("passport");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const _ = require("lodash");

const User = mongoose.model("User");

/**
 * This function gets called when a user submits a search on the display
 * results. The server keeps the current filter state that was passed,
 * along with the Sorting By filter. Then it sends the results
 * of the specified page back to the frontend.
 */
module.exports.search = (req, res, next) => {
  var query = req.body.query;
  var reqURI;

  if(req.body.dates) {
    var dateArr = req.body.dates.split("-");
    query = "(" + query + ")" + "AND" + "(cover_date:[" + dateArr[0] + 
            "+TO+" + dateArr[1] + "])";
  }
  
  if(req.body.size !=null) {
    reqURI = "http://crest-cache-01.cs.fiu.edu:81/articles/article/_search?q=" + 
    query + "&size=" + req.body.size + "&from=" + ((req.body.page - 1) * req.body.size);
  } else {
    reqURI = "http://crest-cache-01.cs.fiu.edu:81/articles/article/_search?q=" + 
    query + "&size=" + 10 + "&from=" + ((req.body.page - 1) * 10);
  }


  if(req.body.sortBy == "most_recent") {
    reqURI = reqURI + "&sort=cover_date:desc";
  }

  if(req.body.sortBy == "relevance") {
    reqURI = reqURI + "&sort=_score:desc";
  }


  request(reqURI, 
    { json: true }, 
    (err, resAPI, body) => {
      if(body == null) {
        res.sendStatus(404);
      }
      res.send(body);
  });
}

module.exports.getArticle = (req, res, next) => {

  reqURI = "http://crest-cache-01.cs.fiu.edu:81/articles/article/" + req.body.article_id;

  request(reqURI, 
    { json: true }, 
    (err, resAPI, body) => {
      if(body == null) {
        res.sendStatus(404);
      }
      res.send(body);
  });
}


/**
 * Gets called from the index.router.js
 * Registers a new user
 */
module.exports.register = (req, res, next) => {
  var user = new User();
  user.fullName = req.body.fullName;
  user.email = req.body.email;
  user.password = req.body.password;
  // don't require user account type on register
  // user.accountType = "user";
  user.articles = [];
  user.search = [];
  user.click = [];
  user.save((err, doc) => {
    if (!err) {
      res.send(doc);
    } else {
      if (err.code == 11000) {
        res.status(422).send(["Duplicate email address found."]);
      } else return next(err);
    }
  });
};

/**
 * Gets called from the index.router.js
 * Authenticates the login information
 */
module.exports.authenticate = (req, res, next) => {
  //call for passport authentication
  passport.authenticate("local", (err, user, info) => {
    //error from passport middleware
    if (err) return res.status(400).json(err);
    else if (user) return res.status(200).json({ token: user.generateJWT() });
    //unknown user or wrong password
    else return res.status(404).json(info);
  })(req, res);
};

/**
 * Gets called from the index.router.js
 * Returns the user profile information
 */
module.exports.userProfile = (req, res, next) => {
  User.findOne({ _id: req._id }, (err, user) => {
    if (!user) {
      return res
        .status(404)
        .json({ status: false, message: "User record not found." });
    } else {
      return res.status(200).json({
        status: true,
        user: _.pick(user, ["fullName", "email","accountType", "articles", "search", "click","feedback"])
      });
    }
  });
};

/**
 * Gets called from the index.router.js
 * Edit User profile
 */
module.exports.updateProfile = (req, res, next) => {

  User.findOne({_id: req._id},
    function(err,user){
      // wrong password
      if (!user.verifyPassword(req.body.currPassword))
        res.status(422).send(["Wrong Password"]);
      // authentication succeeded
      else {
        bcrypt.genSalt(10).then(
          salt => {
            return bcrypt.hash(req.body.password, salt);
          }).then(
          hash => {
            var salt = hash.substring(0, 29);
            User.findOneAndUpdate(
              { _id: req._id },
              {$set: {fullName:req.body.fullName,email:req.body.email,password:hash, saltSecret: salt}},
              {
                // upsert is equal to false because we do not want to insert a user if it doesn't exist
                upsert:false
              },
              
              function(err, updateProfile) {
                if (err) {
                  res.send("Error saving profile");
                  
                } else {
                  res.status(200);
                  res.json(updateProfile);
                }
              }
            );
          }
        ).catch(
          err => {
            res.status(404).json({ status: false, message: "User record not found." });
          }
        );
      }
    }) 
  };

/**
 * Gets called from the index.router.js
 * Authenticates the login information
 */
module.exports.authenticate = (req, res, next) => {
  //call for passport authentication
  passport.authenticate("local", (err, user, info) => {
    //error from passport middleware
    if (err) return res.status(400).json(err);
    else if (user) return res.status(200).json({ token: user.generateJWT() });
    //unknown user or wrong password
    else return res.status(404).json(info);
  })(req, res);
};


/**
 * Gets called from the index.router.js
 * Saves an article to the users profile
 */
module.exports.saveArticle = (req, res, next) => {
  User.findOneAndUpdate(
    { _id: req._id },
    {
      $addToSet: { articles: req.body.articles }
    },
    {
      new: true
    },
    function(err, savedArticle) {
      if (err) {
        res.sendStatus(404); 
      } else {
        res.json(savedArticle);
      }
    }
  );
};

/**
 * Gets called from the index.router.js
 * deletes an article from the users profile
 */
module.exports.unsaveArticle = (req, res, next) => {
  User.findOneAndUpdate(
    { _id: req._id },
    {
      $pull: { articles: {article_id: req.body.articles.article_id }}
    },
    function(err, userAccessed ) {
      if (err) {
        res.send("Error deleting article");
      } else {
        res.json(userAccessed);
      }
    }
  );
};

/**
 * Gets called from the index.router.js
 * Saves a search entry to a users profile
 */
module.exports.saveSearch = (req, res, next) => {
  User.findOneAndUpdate(
    { _id: req._id },
    {
      $addToSet: { search: req.body.search }
    },
    {
      new: true
    },
    function(err, savedSearch) {
      if (err) {
        res.sendStatus(404); 
      } else {
        res.json(savedSearch);
      }
    }
  );
};

/**
 * Gets called from the index.router.js
 * Saves a clicked article to the users profile
 */
module.exports.saveClick = (req, res, next) => {
  User.findOneAndUpdate(
    { _id: req._id },
    {
      $addToSet: { click: req.body.click }
    },
    {
      new: true
    },
    function(err, savedClick) {
      if (err) {
        res.sendStatus(404); 
      } else {
        res.json(savedClick);
      }
    }
  );
};

/**
 * Gets called from the index.router.js
 * Saves a feedback article to the users profile
 */
module.exports.saveFeedback = (req, res, next) => {
  User.findOneAndUpdate(
    { _id: req._id },
    {
      $addToSet: { feedback: req.body.feedback }
    },
    {
      new: true
    },
    function(err, savedFeedback) {
      if (err) {
        res.sendStatus(404); 
      } else {
        res.json(savedFeedback);
      }
    }
  );
};

module.exports.getObjectDate = (req, res, next) => {
  var querySearches = req.body;
  for(let querySearch of querySearches) {
    querySearch.time = new Date(querySearch.time);
    var options = { weekDay: 'long', year: 'numeric', month : 'long', 
                    day : 'numeric', hour : 'numeric', minute : 'numeric',
                    second : 'numeric'};
    querySearch.time = querySearch.time.toLocaleString("en-US", options);
  }
  
  return res.status(200).json({
    status: true,
    result: querySearches
  });
}

/**
 * Merges two or more arrays of objects into a single array by merging objects with the same key
 * (property).
 * @param {string} [key]
 * @param {...*[]} [arrays]
 * @returns {*[]}
 */
module.exports.mergeByKey = (req, res, next) => {
  var key = req.body.key;
  var passParameters = req.body.passParameters;
  var parameters = [req.body.firstParameter, req.body.secondParameter];
  var arrays = [req.body.firstArticle, req.body.secondArticle];
  const array = [];
  const groups = new Map(); // key => [pos in array, [array, of, objects, with, the, same, key]]

  for (let i = 0; i < arrays.length; ++i) {
      for (let j = 0; j < arrays[i].length; ++j) {
          const element = arrays[i][j];
          if (element.hasOwnProperty(key)) {
              const keyValue = element[key];
              if (groups.has(keyValue)) {
                  groups.get(keyValue)[1].push(element);
              } else {
                  if(passParameters !== undefined) {
                    if(element[passParameters] !== undefined) {} else { element[passParameters] = 0;}

                  } else { }
                  if(element[parameters[0]] !== undefined) {} else { element[parameters[0]] = 0;}
                  if(element[parameters[1]] !== undefined) {} else { element[parameters[1]] = 0;}
                  array.push(element);
                  groups.set(keyValue, [array.length - 1, []]);
              }
          } else {
              array.push(element);
          }
      }
  }
  
  for (let group of groups) {
      if (group[1][1].length === 0)
          continue;
      array[group[1][0]] =
          Object.assign.apply(Object, [{}, array[group[1][0]]].concat(group[1][1]));
  }

  return res.status(200).json({
    status: true,
    mergedResult: array
  });
};

/**
 * Gets called from the index.router.js
 * Saves a feedback article to the users profile
 */
module.exports.updateAccountType = (req, res, next) => {

  User.findOneAndUpdate(

    { email: req.body.email },
    {
      $set: { accountType: 'developer' }
    },
    function(err, updateAccountType) {
      if (err) {
        res.sendStatus(404); 
      } else {

        res.json(updateAccountType);
      }
    }
  );
};
