/**
 * Created by jps123 on 6/30/2017.
 */
'use strict';

var _ = require('lodash/object');

var UPPERCASE_RE = /([A-Z])/g;
var LOWERCASE_RE = /([a-z])/g;
var NUMBER_RE = /\D/g;
var SPECIAL_CHAR_RE = /([!"#$%&'()*+,-.:;<=>?@[\]^_`{|}~])/g;

var currentSettings = null;

function doPasswordCheck(password, rules) {

    // assumption: client configuration for passwordRules do not change until restart/refresh of server/client
    var passwordRules = currentSettings != null ? currentSettings : (currentSettings = initRules(rules));
    var uc;
    var lc;
    var sc;
    var numbers;
    if (password && password.match) {
      uc = password.match(UPPERCASE_RE);
      lc = password.match(LOWERCASE_RE);
      sc = password.match(SPECIAL_CHAR_RE);
    }
    if (password && password.replace) {
      numbers = password.replace(NUMBER_RE, '');//strip non digits
    }

    var fails = [];

    var minLenPass = passwordRules.minLength === 0 || password.length >= passwordRules.minLength;

    if(!minLenPass){
        fails.push({type: 'minLength', failMsg: 'must have a minimum of ' + passwordRules.minLength + ' characters.'});
    }

    var maxLenPass = passwordRules.maxLength === 0 || (password.length <= passwordRules.maxLength);

    if(!maxLenPass){
        fails.push({type: 'maxLength' , failMsg: 'must have a maximum of ' + passwordRules.maxLength + ' characters.'});
    }

    if(isRepeating(password, passwordRules.maxAllowedRepeatingChars)){
        fails.push({
            type: 'repeatingChars',
            failMsg: 'should not exceed ' + passwordRules.maxAllowedRepeatingChars + ' repeating characters.'
        });
    }

    var upperCheckPass = passwordRules.uppercaseMinCount === 0  || (uc && uc.length >= passwordRules.uppercaseMinCount) ;

    if(!upperCheckPass){
        fails.push({
            type: 'uppercaseMinCount',
            failMsg: 'should contain ' + passwordRules.uppercaseMinCount +' uppercase characters.'
        });
    }

    var lowerCheckPass = passwordRules.lowercaseMinCount === 0 || (lc && lc.length >= passwordRules.lowercaseMinCount);

    if(!lowerCheckPass){
        fails.push({
            type: 'lowercaseMinCount',
            failMsg: 'should contain ' + passwordRules.lowercaseMinCount +' lowercase characters.'
        });
    }

    var numCheckPass = passwordRules.numberMinCount === 0 || (numbers && numbers.length >= passwordRules.numberMinCount);

    if(!numCheckPass){
        fails.push({
            type:'numberMinCount',
            failMsg: 'should contain ' + passwordRules.numberMinCount +' number characters.'
        });
    }

    var specialCheckPass = passwordRules.specialMinCount === 0 || (sc && sc.length >= passwordRules.specialMinCount);

    if(!specialCheckPass){
        fails.push({
            type: 'specialMinCount',
            failMsg: 'should contain ' + passwordRules.specialMinCount +' special characters.'
        });
    }

    return fails;

}

function initRules(customRules){

    var rules = { //defaults
        maxLength : 50,
        minLength : 8,
        uppercaseMinCount : 3,
        lowercaseMinCount : 3,
        numberMinCount : 2,
        specialMinCount : 2,
        maxAllowedRepeatingChars : 2
    };

    _.assign(rules, customRules); //override rules with customRules

    return rules;

}

function isRepeating(str, maxAllowedRepeatingChars) {

    if(maxAllowedRepeatingChars === 0 ){
        return false; //pass zero
    }

    return new RegExp('([\\w\\d\\?\\-])\\1{' + maxAllowedRepeatingChars  + ',}').test(str);

}

module.exports = {

    /**
     * Defaults are always used if clientConfig is not present.
     * @param clientConfig
     * @returns {*}
     */
    evaluatePassword : function(password, passwordCreationRules){

        //if passwordCreationRules is undefined then defaults will be used. see doPasswordCheck func.
        return doPasswordCheck(password, passwordCreationRules);
    },
    /**
     * Create a simple error string for rule breaches returned by evaluatePassword function.
     * @param ruleBreaches
     */
    createSimpleErrMessageStr : function(ruleBreaches){

        var failMessage = 'Your password didn\'t pass the following criteria: ';

        for(var i = 0 ; i < ruleBreaches.length ; i++){
            var fail = ruleBreaches[i];
            failMessage += '[' + (i+1) + '] '+ fail.failMsg + ' ';
        }

        return failMessage;
    }

};

