const Authentication = require('../modules/authentication');
const Crypto = require('crypto');
const Mongoose = require('mongoose');
const Mailer = require('nodemailer');
const Token = require('../modules/token');

const secret = 'Creepily hooking the gays up since 2008!';

var logger = require('../modules/logger'); 

function generateHmac (userId, expires) {
  var string = String(userId) + '|' + String(expires);
  return Crypto.createHmac('sha1', secret).update(string).digest('hex');
}

function sendMail (options, callback) {
  // create reusable transporter object using the default SMTP transport
  let transporter = Mailer.createTransport({
      host: 'mail.fitz.guru',
      port: 587,
      secure: false, // secure:true for port 465, secure:false for port 587
      auth: {
          user: 'support@fitz.guru',
          pass: 'NotSt@ff3d!'
      }
  });

  callback = typeof callback === 'function' ? callback : (error, info) => {
    if (error) {
      return logger.error(error);
    }

    logger.debug('Message %s sent: %s', info.messageId, info.response);
  };

  // send mail with defined transport object
  transporter.sendMail(options, callback);
}

const ResetSchema = new Mongoose.Schema({
	user: { type: Mongoose.Schema.Types.ObjectId, required: true },
  expires: { type: Date, default: Date.now },
  used: { type: Boolean, default: false },
	updated_at: { type: Date, default: Date.now }
});

const ResetModel = Mongoose.model('resets', ResetSchema);

module.exports = {
  checkReset: (e, id, token, callback) => {
    const promise = new Promise((resolve, reject) => {
      ResetModel.findOne({ _id: id }, (err, result) => {
        if (err) {
          reject(err);
        }
        
        if (result && (token ===  generateHmac(result.user, result.expires)) && (Date.now() <= result.expires)) {
          resolve({ user: String(result.user), err: null });
        } else {
          resolve({ user: false, err: 'The reset link has expired. Please request a new reset link from the login page.'});
        }
      });
    });
    
		promise.then((result) => {
		  if (e) {
  			e.emit('checkReset', null, result);
  		} else if (callback) {
  		  callback(null, result.user);
  		} else {
  		  return true;
  		}
		})
		.catch((err) => {
		  if (e) {
  			e.emit('checkReset', err, null);
  		} else if (callback) {
  		  callback(err, null);
  		} else {
  		  return false;
  		}
		});
  },

  forceReset: (e, user, callback) => {
		const promise = new Promise((forceResetMailResolve, forceResetMailReject) => {
      const forceResetTokenPromise = new Promise((forceResetTokenResolve, forceResetTokenReject) => {  
        var tokenInstance = new ResetModel({ user: user._id, expires: (Date.now() + (72 * 60 * 60 * 1000)) });

        tokenInstance.save((err, result) => {
          if (err) {
            forceResetTokenReject(err);
          }

          if (result) {
            forceResetTokenResolve(result);
          }
        });
      });

      forceResetTokenPromise.then((result) => {
        let resetLink = 'https://timberland.bizdex.cloud/reset/' + encodeURIComponent(result._id) + '/' + encodeURIComponent(generateHmac(result.user, result.expires));
    
        // setup email data with unicode symbols
        let mail = {
          from: '"GCS Vendor Database" <system@timberland.bizdex.cloud>',
          to: user.email,
          subject: 'Mandatory Password Reset',
          text: 'Mandatory Password Reset\r\r\r\r' + user.name.first + ',\r\rA Timberland GCS Vendor Database Administrator has initiated a password reset on your account.\r\rTo complete the action you will need to reset you password <<' + resetLink + '>>.\r\rIf you have any questions, please contact a system administrator.',
          html: '<h2>Mandatory Password Reset</h2><p>' + user.name.first + ',</p><p>A Timberland GCS Vendor Database Administrator has initiated a password reset on your account.</p>To complete the action you will need to <a href="' + resetLink + '">reset your password</a>.</p><p><b>If you have any questions, please contact a system administrator.</b></p>'
        };

        sendMail(mail, (error, info) => {
          if (error) {
            logger.debug('[reset::forceReset] Message Send Error', { error: error });
            forceResetMailResolve({ success: false, message: 'There was an error sending the message', error: error });
          }
        
          if (info) {
            logger.debug('[reset::forceReset] Message sent', { messageId: info.messageId, response: info.response, resetLink: resetLink });
            forceResetMailResolve({ success: true, message: 'Message ' + info.messageId + ' sent: ' + info.response + '.' });
          }
        });
      })
      .catch((err) => {
        logger.debug('[reset::forceReset] There was an error creating the reset token.', { err: err });
        forceResetMailReject(err);
      });
    });
    
		promise.then((result) => {
		  if (e) {
  			e.emit('forceReset', null, result);
  		} else if (callback) {
  		  callback(result);
  		} else {
  		  return true;
  		}
		})
		.catch((err) => {
		  if (e) {
  			e.emit('forceReset', err, null);
  		} else if (callback) {
  		  callback(err);
  		} else {
  		  return false;
  		}
		});
  },

  getResets: (e) => {
		const promise = new Promise((resolve, reject) => {
			ResetModel.find({}, (err, results) => {
        if (err) {
          reject(err);
        }

        if (results) {
          resolve(results);
        }
      });
		});

		promise.then((results) => {
      e.emit('getResets', null, results);
		})
		.catch((err) => {
      e.emit('getResets', err, null);
		});
  },
    
  markUsed: (e, id) => {
    const promise = new Promise((resolve, reject) => {
      ResetModel.findByIdAndUpdate(id, { $set: { used: true } }, (err, result) => {
        if (err) {
          reject(err);
        }
        
        if (result) {
          resolve(result);
        }
      });
    });
    
    promise.then((result) => {
      if (e) {
        e.emit('markUsed', null, result);
      } else {
        logger.debug('[ResetModel::markUsed] Password reset token used', { token: result });
      }
    })
    .catch((err) => {
      if (e) {
        e.emit('markUsed', err, null);
      } else {
        logger.error('[ResetModel::markUsed] Error marking password reset token used', { token: err });
      }
    });
  },
  
  sendNewUser: (e, user) => {
		const promise = new Promise((newUserMailResolve, newUserMailReject) => {
      const newUserTokenPromise = new Promise((newUserTokenResolve, newUserTokenReject) => {  
        var tokenInstance = new ResetModel({ user: user._id, expires: (Date.now() + (72 * 60 * 60 * 1000)) });

        tokenInstance.save((err, result) => {
          if (err) {
            newUserTokenReject(err);
          }

          if (result) {
            newUserTokenResolve(result);
          }
        });
      });

      newUserTokenPromise.then((result) => {
        let setPasswordLink = 'https://timberland.bizdex.cloud/reset/' + encodeURIComponent(result._id) + '/' + encodeURIComponent(generateHmac(result.user, result.expires));
    
        // setup email data with unicode symbols
        let mail = {
          from: '"GCS Vendor Database" <system@timberland.bizdex.cloud>',
          to: user.email,
          subject: 'New User Account Setup',
          text: 'New User Account Setup\r\r\r\r' + user.name.first + ',\r\rA new user has been created for you on the Timberland GCS Vendor Database.\r\rYour username is: <<' + user.userName + '>>.\r\r To complete the setup you will need to create a password <<' + setPasswordLink + '>>.\r\rIf you have any questions, please contact a system administrator.',
          html: '<h2>New User Account Setup</h2><p>' + user.name.first + ',</p><p>A new user has been created for you on the Timberland GCS Vendor Database.</p><p>Your username is: <b>' + user.userName + '</b>.</p>To complete the setup you will need to <a href="' + setPasswordLink + '">create a password</a>.</p><p><b>If you have any questions, please contact a system administrator.</b></p>'
        };

        sendMail(mail, (error, info) => {
          if (error) {
            logger.error('[reset::sendNewUser] Message Send Error', { error: error });
            newUserMailResolve({ success: false, message: 'There was an error sending the message', error: error });
          }
        
          if (info) {
            logger.debug('[reset::sendNewUser] Message %s sent: %s', info.messageId, info.response);
            newUserMailResolve({ success: true, message: 'Message ' + info.messageId + ' sent: ' + info.response + '.' });
          }
        });
      })
      .catch((err) => {
        logger.error('[reset::sendNewUser] There was an error creating the reset token.', { err: err });
        newUserMailReject(err);
      });
    });
    
		promise.then((result) => {
		  if (e) {
  			e.emit('sendNewUser', null, result);
  		} else {
  		  return true;
  		}
		})
		.catch((err) => {
		  if (e) {
  			e.emit('sendNewUser', err, null);
  		} else {
  		  return false;
  		}
		});
  },
  
  sendReset: (e, user) => {

    const resetTokenPromise = new Promise((resetTokenResolve, resetTokenReject) => {  

      var tokenInstance = new ResetModel({ user: user._id, expires: (Date.now() + (30 * 60 * 1000)) });

      tokenInstance.save((err, result) => {
        if (err) {
          resetTokenReject(err);
        }

        if (result) {
          resetTokenResolve(result);
        }
      });
    });
    
    resetTokenPromise.then((data) => {
      var token = generateHmac(data.user, data.expires);
      var tokenId = data._id;
      
      const sendMailPromise = new Promise((sendMailResolve, sendMailReset) => {
      
        var resetLink = 'https://timberland.bizdex.cloud/reset/' + encodeURIComponent(tokenId) + '/' + encodeURIComponent(token);
  
        // setup email data with unicode symbols
        var mail = {
          from: '"GCS Vendor Database" <system@timberland.bizdex.cloud>',
          to: user.email,
          subject: 'Password Reset Request',
          text: user.name.first + ',\r\rA request has been received to reset your password. If you initiated please visit <<' + resetLink + '>>.\r\rIf you did not initiate this request, you can safely ignore this email.',
          html: '<h2>Password Reset Request</h2><p>' + user.name.first + ',</p><p>A request has been received to reset your password. If you initiated this request, <a href="' + resetLink + '">click here to reset your password</a>.</p><p><b>If you did not initiate this request, you can safely ignore this email.</b></p>'
        };

        sendMail(mail, (err, info) => {
          if (err) {
            var error = { msg: '[reset::sendReset] There was an error sending the reset email.', err: err };
            logger.error('[reset::sendReset] Message Send Error', { err: err });
            sendMailResolve({ success: false, message: 'There was an error requesting the password reset.', error: error });
          }
      
          if (info) {
            var message = 'Message ' + info.messageId + ' sent: ' + info.response + '.';
            logger.debug('[reset::sendReset] ' + message);
            sendMailResolve({ success: true, message: 'The password reset request was successfully completed.', response: message });
          }
        });
      });

      sendMailPromise.then((result) => {
        e.emit('sendReset', null, result);
      })
      .catch((err) => {
        e.emit('sendReset', err, null);
      });
    })
    .catch((err) => {
      var error = { msg: '[reset::sendReset] There was an error creating the reset token.', err: err };
      logger.debug(error.msg, { err: err });
      e.emit('sendReset', null, { success: false, message: 'There was an error requesting the password reset.', error: error });
    });
  }
};
