package de.champonthis.buntspecht.controller.validation;

import java.util.ArrayList;
import java.util.List;

import org.passay.CharacterRule;
import org.passay.EnglishCharacterData;
import org.passay.LengthRule;
import org.passay.PasswordData;
import org.passay.PasswordValidator;
import org.passay.Rule;
import org.passay.RuleResult;
import org.passay.RuleResultDetail;
import org.passay.WhitespaceRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import de.champonthis.buntspecht.businesslogic.SystemPropertyManager;
import de.champonthis.buntspecht.controller.model.UserPasswordModel;

@Component
public class PasswordModelValidator implements Validator {

    @Autowired
    private SystemPropertyManager systemPropertyManager;

    public static final String SYSTEM_PROPERTY_PASSWORD_RULE_WHITESPACE = "password.rule.whitespace";
    public static final String SYSTEM_PROPERTY_PASSWORD_RULE_LENGTH = "password.rule.length";
    public static final String SYSTEM_PROPERTY_PASSWORD_RULE_UPPERCASE = "password.rule.uppercase";
    public static final String SYSTEM_PROPERTY_PASSWORD_RULE_DIGIT = "password.rule.digit";
    public static final String SYSTEM_PROPERTY_PASSWORD_RULE_SPECIAL = "password.rule.special";

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.isAssignableFrom(UserPasswordModel.class);
    }

    @Override
    public void validate(Object target, Errors errors) {
        UserPasswordModel passwordModel = (UserPasswordModel) target;

        List<Rule> rules = new ArrayList<Rule>();

        if (systemPropertyManager.getBoolean(SYSTEM_PROPERTY_PASSWORD_RULE_WHITESPACE, true)) {
            rules.add(new WhitespaceRule());
        }

        int length = systemPropertyManager.getInteger(SYSTEM_PROPERTY_PASSWORD_RULE_LENGTH, 8);
        if (length > 0) {
            rules.add(new LengthRule(length, 4096));
        }

        int uppercase = systemPropertyManager.getInteger(SYSTEM_PROPERTY_PASSWORD_RULE_UPPERCASE, 1);
        if (uppercase > 0) {
            rules.add(new CharacterRule(EnglishCharacterData.UpperCase, uppercase));
        }

        int digit = systemPropertyManager.getInteger(SYSTEM_PROPERTY_PASSWORD_RULE_DIGIT, 1);
        if (digit > 0) {
            rules.add(new CharacterRule(EnglishCharacterData.Digit, digit));
        }

        int special = systemPropertyManager.getInteger(SYSTEM_PROPERTY_PASSWORD_RULE_SPECIAL, 1);
        if (special > 0) {
            rules.add(new CharacterRule(EnglishCharacterData.Special, special));
        }

        PasswordValidator validator = new PasswordValidator(rules);
        PasswordData password = new PasswordData(passwordModel.getPassword());
        RuleResult result = validator.validate(password);

        if (!result.isValid()) {
            for (RuleResultDetail ruleResultDetail : result.getDetails()) {
                errors.rejectValue("password", ruleResultDetail.getErrorCode());
            }
        }

        if (!passwordModel.getPassword().equals(passwordModel.getPassword2())) {
            errors.rejectValue("password2", "NOT_MATCH");
        }
    }

}
