# Source: controller/token_controller.js

    'use strict';

    var async = require('async'),
    	appSettings,
    	appenvironment,
    	bcrypt = require('bcrypt'),
    	CoreController,
    	CoreUtilities,
    	ControllerHelper = require('periodicjs.core.controller'),
    	CoreMailer = require('periodicjs.core.mailer'),
    	Utilities = require('periodicjs.core.utilities'),
    	jwt = require('jsonwebtoken'),
    	loginExtSettings,
    	logger,
    	mongoose,
    	User,
    	path = require('path'),
    	passport;

    // Utility Functions
    var waterfall = function (array, cb) {
    	async.waterfall(array, cb);
    };
    var encode = function (data) {
    	return jwt.sign(data, loginExtSettings.token.secret);
    };

    var decode = function (data, cb) {
    	jwt.verify(data, loginExtSettings.token.secret, {}, function (err, decoded_token) {
    		if (err) {
    			console.log('Error from JWT.verify', err.name);
    			console.log('Error from JWT.verify', err.message);
    			cb(err);
    		}
    		else {
    			cb(null, decoded_token);
    		}
    	});
    };

    var hasExpired = function (token_expires_millis) {
    	var now = new Date();
    	var diff = (now.getTime() - token_expires_millis);
    	return diff > 0;
    };

    var invalidateUserToken = function (req, res, next, cb) {
    	var token = req.controllerData.token;
    	User.findOne({
    		'attributes.reset_token': token
    	}, function (err, usr) {
    		if (err) {
    			console.log('error finding the user for invalidate token fn');
    			cb(err, null);
    		}
    		else {
    			usr.attributes.reset_token = '';
    			usr.attributes.reset_token_link = '';
    			usr.attributes.reset_token_expires_millis = 0;
    			cb(false, req, res, next, usr);
    		}
    	});
    };

    var resetPassword = function (req, res, next, user, cb) {
    	var err;
    	//console.log('loginExtSettings', loginExtSettings);
    	if (req.body.password) {
    		if (req.body.password !== req.body.passwordconfirm) {
    			err = new Error('Passwords do not match');
    			req.flash('error', err);
    			cb(err, null);
    		}
    		else if (req.body.password === undefined || req.body.password.length < loginExtSettings.new_user_validation.length_of_password) {
    			err = new Error('Password is too short');
    			req.flash('error', err);
    			cb(err, null);
    		}
    		else {
    			var salt = bcrypt.genSaltSync(10),
    				hash = bcrypt.hashSync(req.body.password, salt);
    			user.password = hash;
    			cb(null, user, req);
    		}
    	}
    };

    /**
     * description The save user function has two special fn calls on the model to mark the properties on it as changed/modified this gets around some werid edge cases when its being updated in memory but not save in mongo
     *
     */
    function saveUser(user, req, cb) {
    	user.markModified('attributes');
    	user.markModified('password');
    	user.save(function (err, usr) {
    		if (err) {
    			cb(err, null);
    		}
    		cb(null, usr, req);
    	});
    }

    var getUser = function (req, res, next, cb) {
    	User.findOne({
    		email: req.body.email
    	}, function (err, user) {
    		if (err) {
    			cb(err, null);
    		}
    		else if (user) {
    			cb(false, user, req);
    		}
    		else {
    			req.flash('error', 'No user with that email found!');
    			cb(new Error('No user with that email found.'), null);
    		}
    	});
    };

    var generateToken = function (user, req, cb) {
    	//Generate reset token and URL link; also, create expiry for reset token
    	//make sure attributes exists || create it via merge
    	var salt = bcrypt.genSaltSync(10);
    	var now = new Date();
    	var expires = new Date(now.getTime() + (loginExtSettings.token.resetTokenExpiresMinutes * 60 * 1000)).getTime();
    	user.attributes = {};
    	user.attributes.reset_token = encode({
    		email: user.email,
    		apikey: user.apikey
    	});
    	user.attributes.reset_token_link = CoreUtilities.makeNiceName(bcrypt.hashSync(user.attributes.reset_token, salt));
    	user.attributes.reset_token_expires_millis = expires;

    	//TODO: Look into why mongoose properties
    	//are not being saved during async fn calls
    	user.markModified('attributes');
    	user.save(function (err) {
    		if (err) {
    			cb(err, null);
    		}
    		cb(null, user, req);
    	});
    };

    // create a func for the mail options

    var emailForgotPasswordLink = function (user, req, cb) {
    	CoreController.getPluginViewDefaultTemplate({
    			viewname: 'email/user/forgot_password_link',
    			themefileext: appSettings.templatefileextension
    		},
    		function (err, templatepath) {
    			if (err) {
    				cb(err);
    			}
    			else {
    				// console.log('user for forgot password', user);
    				if (templatepath === 'email/user/forgot_password_link') {
    					templatepath = path.resolve(process.cwd(), 'node_modules/periodicjs.ext.login/views', templatepath + '.' + appSettings.templatefileextension);
    				}
    				CoreMailer.sendEmail({
    					appenvironment: appenvironment,
    					to: user.email,
    					replyTo: appSettings.adminnotificationemail,
    					subject: appSettings.name + ' - Reset your password',
    					emailtemplatefilepath: templatepath,
    					emailtemplatedata: {
    						user: user,
    						appname: appSettings.name,
    						hostname: req.headers.host
    					}
    				}, cb);
    			}
    		}
    	);
    	// cb(null, options);
    };

    var emailResetPasswordNotification = function (user, req, cb) {
    	CoreController.getPluginViewDefaultTemplate({
    			viewname: 'email/user/reset_password_notification',
    			themefileext: appSettings.templatefileextension
    		},
    		function (err, templatepath) {
    			if (err) {
    				cb(err);
    			}
    			else {
    				// console.log('user for forgot password', user);
    				if (templatepath === 'email/user/reset_password_notification') {
    					templatepath = path.resolve(process.cwd(), 'node_modules/periodicjs.ext.login/views', templatepath + '.' + appSettings.templatefileextension);
    				}
    				CoreMailer.sendEmail({
    					appenvironment: appenvironment,
    					to: user.email,
    					replyTo: appSettings.adminnotificationemail,
    					subject: appSettings.name + ' - Password reset notification',
    					emailtemplatefilepath: templatepath,
    					emailtemplatedata: {
    						user: user,
    						appname: appSettings.name,
    						hostname: req.headers.host
    					}
    				}, cb);
    			}
    		}
    	);
    	// cb(null, options);
    };

    //Post to auth/forgot with the users email
    var forgot = function (req, res, next) {
    	var arr = [
    		function (cb) {
    			cb(null, req, res, next);
    		},
    		getUser,
    		generateToken,
    		emailForgotPasswordLink
    	];

    	waterfall(arr,
    		function (err /*, results*/ ) {
    			if (err) {
    				req.flash('error', err.message);
    				res.redirect('/auth/forgot');
    			}
    			else {
    				req.flash('info', 'Password reset instructions were sent to your email address');
    				res.redirect(loginExtSettings.settings.authLoginPath);
    			}
    		});
    };

    var get_token = function (req, res, next) {
    	req.controllerData = (req.controllerData) ? req.controllerData : {};

    	User.findOne({
    		'attributes.reset_token_link': req.params.token
    	}, function (err, user_with_token) {
    		if (err) {
    			req.flash('error', err.message);
    			res.redirect(loginExtSettings.settings.authLoginPath);
    		}
    		else if (!user_with_token || !user_with_token.attributes.reset_token) {
    			req.flash('error', 'invalid reset token');
    			res.redirect(loginExtSettings.settings.authLoginPath);
    		}
    		else if (hasExpired(user_with_token.attributes.reset_token_expires_millis)) {
    			req.flash('error', 'Password reset token is has expired.');
    			res.redirect(loginExtSettings.settings.authLoginPath);
    		}
    		else {
    			req.controllerData.token = user_with_token.attributes.reset_token;
    			next();
    		}
    	});
    };

    //GET if the user token is vaild show the change password page
    var reset = function (req, res) {
    	var token = req.controllerData.token,
    		// current_user,
    		decode_token;

    	decode(token, function (err, decode) {
    		if (err) {
    			CoreController.handleDocumentQueryErrorResponse({
    				err: err,
    				res: res,
    				req: req,
    				errorflash: err.message
    			});
    		}
    		else {
    			decode_token = decode;
    			//Find the User by their token
    			User.findOne({
    				'attributes.reset_token': token
    			}, function (err, found_user) {
    				if (err || !found_user) {
    					req.flash('error', 'Password reset token is invalid.');
    					res.redirect(loginExtSettings.settings.authLoginPath);
    				}
    				// current_user = found_user;
    				//Check to make sure token hasn't expired

    				//Check to make sure token is valid and sign by us
    				else if (found_user.email !== decode_token.email && found_user.api_key !== decode_token.api_key) {
    					req.flash('error', 'This token is not valid please try again');
    					res.redirect(loginExtSettings.settings.authLoginPath);
    				}
    				else {

    					CoreController.getPluginViewDefaultTemplate({
    							viewname: 'user/reset',
    							themefileext: appSettings.templatefileextension,
    							extname: 'periodicjs.ext.login'
    						},
    						function (err, templatepath) {
    							CoreController.handleDocumentQueryRender({
    								res: res,
    								req: req,
    								renderView: templatepath,
    								responseData: {
    									pagedata: {
    										title: 'Reset Password',
    										current_user: found_user
    									},
    									user: req.user
    								}
    							});
    						});
    				}

    			});

    		}
    	});

    };

    //POST change the users old password to the new password in the form
    var token = function (req, res, next) {
    	var user_token = req.controllerData.token;
    	waterfall([
    			function (cb) {
    				cb(null, req, res, next);
    			},
    			invalidateUserToken,
    			resetPassword,
    			saveUser,
    			emailResetPasswordNotification
    		],
    		function (err /*, results*/ ) {
    			if (err) {
    				req.flash('error', err.message);
    				res.redirect('/auth/reset/' + user_token);
    			}
    			else {
    				req.flash('success', 'Password Sucessfully Changed!');
    				res.redirect(loginExtSettings.settings.authLoginPath);
    			}

    		});
    };

    var tokenController = function (resources, passportResources) {
    	appSettings = resources.settings;
    	CoreController = new ControllerHelper(resources);
    	CoreUtilities = new Utilities(resources);
    	loginExtSettings = passportResources.loginExtSettings;
    	logger = resources.logger;
    	mongoose = resources.mongoose;
    	passport = passportResources.passport;
    	User = mongoose.model('User');
    	appenvironment = appSettings.application.environment;
    	return {
    		forgot: forgot,
    		reset: reset,
    		get_token: get_token,
    		token: token
    	};
    };

    module.exports = tokenController;