/*******************************************************************************
 * Copyright 2Fi Business Solutions Ltd.
 *
 * This code is copyrighted. Under no circumstances should any party, people,
 * or organization should redistribute any portions of this code in any form,
 * either verbatim or through electronic media, to any third parties, unless
 * under explicit written permission by 2Fi Business Solutions Ltd.
 ******************************************************************************/
package com.ffii.core.utils;

import java.util.regex.Pattern;

public abstract class PasswordUtils {

	private static final Pattern PATTERN_DIGITS = Pattern.compile("[0-9]");
	private static final Pattern PATTERN_A2Z_LOWER = Pattern.compile("[a-z]");
	private static final Pattern PATTERN_A2Z_UPPER = Pattern.compile("[A-Z]");
	// private static final String A2Z_LOWER = "abcdefghijklmnopqrstuvwxyz";
	// private static final String A2Z_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	// private static final String DIGITS = "0123456789";
	// private static final String SPECIAL_CHARS = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
	private static Pattern PATTERN_SPECIAL_CHARS = Pattern.compile("[!\"#$%&'()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~]");

	/*
	 * Ref: https://www.owasp.org/index.php/Password_special_characters
	 * without space character
	 */

	public static final boolean checkPwd(String pwd, IPasswordRule rule, String username) {
		if (pwd == null) return false;
		if (pwd.length() < rule.getMin()) return false;

		if (rule.needSpecialCharacter() && !containsSpecialCharacters(pwd)) 
			return false;
		if (rule.needNumberAndAlphabetic() && !containsAlphabeticAndNumericCharacters(pwd)) 
		 	return false;
		if (rule.needNotContainUsername() && containsReferenceSubstring(pwd, username)) 
			return false;
		if (rule.needNotContainThreeConsecutiveCharacters() && containsThreeConsecutiveCharacters(pwd))
			return false;

		return true;
	}

	private static boolean containsSpecialCharacters(String input) {
        return PATTERN_SPECIAL_CHARS.matcher(input).find();
    }

    private static boolean containsAlphabeticAndNumericCharacters(String input) {
        boolean containsAlphabetic = PATTERN_A2Z_LOWER.matcher(input).find() || PATTERN_A2Z_UPPER.matcher(input).find();
        boolean containsNumeric = PATTERN_DIGITS.matcher(input).find();
        return containsAlphabetic && containsNumeric;
    }

	private static boolean containsReferenceSubstring(String input, String username) {
        return input.contains(username);
    }

	private static boolean containsThreeConsecutiveCharacters(String input) {
        for (int i = 0; i < input.length() - 2; i++) {
            char currentChar = input.charAt(i);
            char nextChar = input.charAt(i + 1);
            char nextNextChar = input.charAt(i + 2);
            if (currentChar == nextChar && nextChar == nextNextChar) {
                return true;
            }
        }
        return false;
    }

	/*public static String genPwd(IPasswordRule rule, String username) {
		int length = rule.getMin();

		StringBuilder password = new StringBuilder(length);
		Random random = new Random(System.nanoTime());

		List<String> charCategories = new ArrayList<>(4);
		if (rule.needSpecialCharacter()) charCategories.add(SPECIAL_CHARS);
		if (rule.needNumberAndAlphabetic()){
			charCategories.add(A2Z_UPPER);
			charCategories.add(A2Z_LOWER);
			charCategories.add(DIGITS);
		} 
		
		
		if (rule.needSpecialChar()) charCategories.add(SPECIAL_CHARS);

		for (int i = 0; i < length; i++) {
			String charCategory = charCategories.get(i % charCategories.size());
			char randomChar = charCategory.charAt(random.nextInt(charCategory.length()));
			if (password.length() > 0)
				password.insert(random.nextInt(password.length()), randomChar);
			else
				password.append(randomChar);
		}

		return password.toString();
	}*/

	public static interface IPasswordRule {
		public Integer getMin();

		public boolean needNumberAndAlphabetic();

		public boolean needSpecialCharacter();

		public boolean needNotContainUsername();

		public boolean needNotContainThreeConsecutiveCharacters();
	}
}
