/*******************************************************************************
 * 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.ArrayList;
import java.util.List;
import java.util.Random;
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";

	/*
	 * Ref: https://www.owasp.org/index.php/Password_special_characters
	 * without space character
	 */
	private static final String SPECIAL_CHARS = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
	private static Pattern PATTERN_SPECIAL_CHARS = Pattern.compile("[!\"#$%&'()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~]");

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

		if (rule.needNumberChar() && !PATTERN_DIGITS.matcher(pwd).find()) return false;
		if (rule.needUpperEngChar() && !PATTERN_A2Z_UPPER.matcher(pwd).find()) return false;
		if (rule.needLowerEngChar() && !PATTERN_A2Z_LOWER.matcher(pwd).find()) return false;
		if (rule.needSpecialChar() && !PATTERN_SPECIAL_CHARS.matcher(pwd).find()) return false;

		return true;
	}

	public static String genPwd(IPasswordRule rule) {
		int length = rule.getMin();

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

		List<String> charCategories = new ArrayList<>(4);
		if (rule.needLowerEngChar()) charCategories.add(A2Z_LOWER);
		if (rule.needUpperEngChar()) charCategories.add(A2Z_UPPER);
		if (rule.needNumberChar()) 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 int getMin();

		public int getMax();

		public boolean needNumberChar();

		public boolean needUpperEngChar();

		public boolean needLowerEngChar();

		public boolean needSpecialChar();
	}
}
