using System.Text.RegularExpressions;
using System.Threading.Tasks;

//https://stephenhaunts.com/2014/01/31/checking-the-strength-of-a-password/
//https://github.com/danielmiessler/SecLists/tree/master/Passwords

namespace Knoks.Framework.Security
{
    public sealed class PasswordStrength : IPasswordStrength
    {
        private readonly static string[] _weakPasswordList = { "welcome", "123456", "password", "12345678", "qwerty", "123456789", "12345", "1234", "111111", "1234567", "dragon", "123123", "baseball", "abc123", "football", "monkey", "letmein", "696969", "shadow", "master", "666666", "qwertyuiop", "123321", "mustang", "1234567890", "michael", "654321", "pussy", "superman", "1qaz2wsx", "7777777", "fuckyou", "121212", "000000", "qazwsx", "123qwe", "killer", "trustno1", "jordan", "jennifer", "zxcvbnm", "asdfgh", "hunter", "buster", "soccer", "harley", "batman", "andrew", "tigger", "sunshine", "iloveyou", "fuckme", "2000", "charlie", "robert", "thomas", "hockey", "ranger", "daniel", "starwars", "klaster", "112233", "george", "asshole", "computer", "michelle", "jessica", "pepper", "1111", "zxcvbn", "555555", "11111111", "131313", "freedom", "777777", "pass", "fuck", "maggie", "159753", "aaaaaa", "ginger", "princess", "joshua", "cheese", "amanda", "summer", "love", "ashley", "6969", "nicole", "chelsea", "biteme", "matthew", "access", "yankees", "987654321", "dallas", "austin", "thunder", "taylor", "matrix" };

        public async Task<PasswordScore> CheckStrength(string password)
        {
            return await Task.Run(() =>
            {
                int score = 0;

                if (string.IsNullOrEmpty(password))
                {
                    return PasswordScore.Blank;
                }

                if (IsPasswordInWeakList(password))
                {
                    return PasswordScore.Weak;
                }

                if (password.Length < 1)
                {
                    return PasswordScore.Blank;
                }

                if (password.Length < 4)
                {
                    return PasswordScore.VeryWeak;
                }

                if (password.Length >= 8)
                {
                    score++;
                }

                if (password.Length >= 10)
                {
                    score++;
                }

                if (Regex.Match(password, @"\d", RegexOptions.ECMAScript).Success)
                {
                    score++;
                }

                if (Regex.Match(password, @"[a-z]", RegexOptions.ECMAScript).Success &&
                    Regex.Match(password, @"[A-Z]", RegexOptions.ECMAScript).Success)
                {
                    score++;
                }

                if (Regex.Match(password, @"[!,@,#,$,%,^,&,*,?,_,~,-,£,(,)]", RegexOptions.ECMAScript).Success)
                {
                    score++;
                }

                return (PasswordScore)score;
            });
        }

        private static bool IsPasswordInWeakList(string password)
        {
            foreach (string weakPassword in _weakPasswordList)
            {
                if (string.Compare(password, weakPassword, System.StringComparison.OrdinalIgnoreCase) == 0)
                {
                    return true;
                }

                if (PerformSubstitutions(weakPassword, password))
                {
                    return true;
                }
            }

            return false;
        }

        private static bool PerformSubstitutions(string weakPassword, string password)
        {
            char[] vowels = { 'A', 'a', 'e', 'i', 'o', 's', 'S' };
            char[] vowelSubstitution = { '4', '@', '3', '1', '0', '$', '5' };

            ReplaceLettersWithSubStitutions(password, vowels, vowelSubstitution);

            if (string.Compare(ReplaceLettersWithSubStitutions(weakPassword, vowels, vowelSubstitution), password, System.StringComparison.OrdinalIgnoreCase) == 0)
            {
                return true;
            }

            char[] qwerty = { 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P' };
            char[] qwertySubstitution = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };

            if (string.Compare(ReplaceLettersWithSubStitutions(weakPassword, qwerty, qwertySubstitution), password, System.StringComparison.OrdinalIgnoreCase) == 0)
            {
                return true;
            }

            return false;
        }

        private static string ReplaceLettersWithSubStitutions(string password, char[] original, char[] substitution)
        {
            string newPassword = string.Empty;

            foreach (char c in password)
            {
                bool numberAdded = false;

                for (int q = 0; q < original.Length; q++)
                {
                    if (c == original[q])
                    {
                        newPassword = newPassword + substitution[q];
                        numberAdded = true;
                        break;
                    }
                }

                if (!numberAdded)
                {
                    newPassword = newPassword + c;
                }
            }

            return newPassword;
        }
    }
}