import {IPasswordValidityResult} from "../interfaces/password-validity-result";

export class PasswordStrengthChecker {
  private static readonly defaultOptions = [
    {
      value: 0,
      text: "Too weak",
      minDiversity: 0,
      minLength: 0
    },
    {
      value: 1,
      text: "Weak",
      minDiversity: 2,
      minLength: 6
    },
    {
      value: 2,
      text: "Strong",
      minDiversity: 4,
      minLength: 8
    },
    {
      value: 3,
      text: "Excellent",
      minDiversity: 4,
      minLength: 10
    }
  ];
  private static readonly defaultAllowedSymbols = "!\"#\$%&'\(\)\*\+,-\./:;<=>\?@\[\\\\\\]\^_`\{|\}~";

  public static validate(password: string, options = this.defaultOptions, allowedSymbols = this.defaultAllowedSymbols): IPasswordValidityResult {
    const passwordCopy = password || "";

    options[0].minDiversity = 0;
    options[0].minLength = 0;

    const rules = [
      {
        regex: "[a-z]",
        message: "lowercase"
      },
      {
        regex: "[A-Z]",
        message: "uppercase"
      },
      {
        regex: "[0-9]",
        message: "number"
      },
    ];

    if (allowedSymbols) {
      rules.push({
        regex: `[${allowedSymbols}]`,
        message: "symbol"
      });
    }

    const strength: any = {};

    strength.contains = rules
      .filter(rule => new RegExp(`${rule.regex}`).test(passwordCopy))
      .map(rule => rule.message);

    strength.length = passwordCopy.length;

    const fulfilledOptions = options
      .filter(option => strength.contains.length >= option.minDiversity)
      .filter(option => strength.length >= option.minLength)
      .sort((o1, o2) => o2.value - o1.value)
      .map(option => ({value: option.value, text: option.text}));

    Object.assign(strength, fulfilledOptions[0]);

    return strength;
  }
}
