using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace DeviceRepair.Models
{
    /// <summary>
    /// 密码策略对象
    /// (目前没有存入后台数据库，目前基于客户端判断)
    /// </summary>
    public class PasswordStrategy
    {
        private static PasswordStrategy manager;
        public static PasswordStrategy Instance
        {
            get
            {
                if (manager == null)
                    manager = new PasswordStrategy();
                return manager;
            }
        }

        #region Fields & Property

        /// <summary>
        /// 默认的特殊字符集
        /// </summary>
        private char[] DefaultSpecialCharacters = new char[]
        {
            '~','`','!','@','#','$','%','^','&','*','(',')','-','_','+','-','[',']','{','}','|','\\',':',';','"','\'','<','>','.','/','?'
        };

        /// <summary>
        /// 最小密码长度
        /// </summary>
        public int PasswordMinLength
        {
            get;
            set;
        }
        /// <summary>
        /// 密码策略类型
        /// </summary>
        public PasswordStrategyType StrategyType
        {
            get;
            set;
        }
        /// <summary>
        /// 最小密码策略组合类型种类
        /// </summary>
        public int MinPasswordStrategyTypeCounts
        {
            get;
            set;
        }
        /// <summary>
        /// 密码有效期限
        /// （以天为单位）
        /// </summary>
        public int PasswordValidDays
        {
            get; set;
        }
        /// <summary>
        /// 密码错误连续重试次数
        /// （超过连续重试次数后，账号将被锁定）
        /// </summary>
        public int PasswordContinueRetryCounts
        {
            get; set;
        }
        /// <summary>
        /// 密码修改不允许重复的次数
        /// （默认：连续5次密码修改不允许相同）
        /// </summary>
        public int PasswordAlterUnRepeatCounts
        {
            get; set;
        }
        /// <summary>
        /// 系统允许的屏幕最大闲置时间（以分钟为单位）
        /// （超过规定时间，系统屏幕将被锁定，需要账号登陆解锁屏幕）
        /// </summary>
        public int ScreenLockTimes
        {
            get; set;
        }
        #endregion

        #region Ctor

        public PasswordStrategy(PasswordStrategyType strategyType = PasswordStrategyType.UpperCaseLetter | PasswordStrategyType.LowerCaseLetter | PasswordStrategyType.Number | PasswordStrategyType.SpecialCharacter,
            int passwordMinLength = 8, int minPasswordStrategyTypeCounts = 3, int passwordValidDays = 90, int passwordContinueRetryCounts = 5,
            int passwordAlterUnRepeatCounts = 5, int screenLockTimes = 15)
        {
            StrategyType = strategyType;
            PasswordMinLength = passwordMinLength;
            MinPasswordStrategyTypeCounts = minPasswordStrategyTypeCounts;
            PasswordValidDays = passwordValidDays;
            PasswordContinueRetryCounts = passwordContinueRetryCounts;
            PasswordAlterUnRepeatCounts = passwordAlterUnRepeatCounts;
            ScreenLockTimes = screenLockTimes;
        }

        #endregion

        #region Methods

        /// <summary>
        /// 密码校验
        /// </summary>
        /// <param name="password"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public bool Validate(string password, out string msg)
        {
            try
            {
                msg = "密码校验成功！";

                if (string.IsNullOrEmpty(password))
                {
                    msg = "密码为空！";
                    return false;
                }
                if (password.Length < PasswordMinLength)
                {
                    msg = $"密码至少位{PasswordMinLength}位！";
                    return false;
                }

                #region 校验密码强度
                int iCurrentPasswordIntensity = 0;

                if ((StrategyType & PasswordStrategyType.UpperCaseLetter) != 0)
                {
                    //策略中要求大写字母验证
                    if (CheckCharacter(password, PasswordStrategyType.UpperCaseLetter))
                    {
                        iCurrentPasswordIntensity++;
                    }
                }
                if ((StrategyType & PasswordStrategyType.LowerCaseLetter) != 0)
                {
                    //策略中要求小写字母验证
                    if (CheckCharacter(password, PasswordStrategyType.LowerCaseLetter))
                    {
                        iCurrentPasswordIntensity++;
                    }
                }
                if ((StrategyType & PasswordStrategyType.Number) != 0)
                {
                    //策略中要求数字验证
                    if (CheckCharacter(password, PasswordStrategyType.Number))
                    {
                        iCurrentPasswordIntensity++;
                    }
                }
                if ((StrategyType & PasswordStrategyType.SpecialCharacter) != 0)
                {
                    //策略中要求特殊字符验证
                    if (CheckCharacter(password, PasswordStrategyType.SpecialCharacter))
                    {
                        iCurrentPasswordIntensity++;
                    }
                }
                if (iCurrentPasswordIntensity < MinPasswordStrategyTypeCounts)
                {
                    msg = $"密码不合符密码强度策略（大写字母/小写字母/数字/特殊字符中最好满足其中{MinPasswordStrategyTypeCounts}种）！";
                    return false;
                }
                #endregion

                return true;
            }
            catch (Exception ex)
            {
                throw new Exception($"密码校验时发生异常：{ex.Message}");
            }
        }

        /// <summary>
        /// 校验密码是否符合单一密码策略
        /// </summary>
        /// <param name="password"></param>
        /// <param name="strategyType"></param>
        /// <returns></returns>
        private bool CheckCharacter(string password, PasswordStrategyType strategyType)
        {
            string regexExpresion = string.Empty;
            switch (strategyType)
            {
                case PasswordStrategyType.UpperCaseLetter:
                    regexExpresion = "[A-Z]";
                    break;
                case PasswordStrategyType.LowerCaseLetter:
                    regexExpresion = "[a-z]";
                    break;
                case PasswordStrategyType.Number:
                    regexExpresion = "[0-9]";
                    break;
                case PasswordStrategyType.SpecialCharacter:
                    if ((password.Where(A => { return DefaultSpecialCharacters.Contains(A); })?.Count() ?? 0) > 0)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                default:
                    break;
            }
            return Regex.IsMatch(password, regexExpresion);
        }

        #region 随机产生符合当前密码策略的密码

        /// <summary>
        /// 随机产生符合当前密码策略的密码
        /// </summary>
        /// <returns></returns>
        public string GenRandomPassword()
        {
            string vPwdTmp = string.Empty;
            if ((StrategyType & PasswordStrategyType.UpperCaseLetter) != 0)
            {
                vPwdTmp += GetRandomCharByStrategy(PasswordStrategyType.UpperCaseLetter, (new Random(~unchecked((int)DateTime.Now.Ticks))).Next(3, 5));
            }
            if ((StrategyType & PasswordStrategyType.LowerCaseLetter) != 0)
            {
                vPwdTmp += GetRandomCharByStrategy(PasswordStrategyType.LowerCaseLetter, (new Random(~unchecked((int)DateTime.Now.Ticks))).Next(3, 5));
            }
            if ((StrategyType & PasswordStrategyType.Number) != 0)
            {
                vPwdTmp += GetRandomCharByStrategy(PasswordStrategyType.Number, (new Random(~unchecked((int)DateTime.Now.Ticks))).Next(3, 5));
            }
            if ((StrategyType & PasswordStrategyType.SpecialCharacter) != 0)
            {
                vPwdTmp += GetRandomCharByStrategy(PasswordStrategyType.SpecialCharacter, (new Random(~unchecked((int)DateTime.Now.Ticks))).Next(3, 5));
            }
            if (vPwdTmp.Length < PasswordMinLength)
            {
                vPwdTmp += RandomString.GetRandomString(PasswordMinLength - vPwdTmp.Length,
                  ((StrategyType & PasswordStrategyType.Number) != 0), ((StrategyType & PasswordStrategyType.LowerCaseLetter) != 0),
                  ((StrategyType & PasswordStrategyType.UpperCaseLetter) != 0),
                  ((StrategyType & PasswordStrategyType.SpecialCharacter) != 0));
            }
            return RandomString.RandomChangeStringSequence(vPwdTmp);
        }

        /// <summary>
        /// 按照密码策略随机产生指定位数的字符
        /// </summary>
        /// <param name="pwdStrategyType">密码策略</param>
        /// <param name="iRandomLength">随机位数</param>
        /// <returns></returns>
        private string GetRandomCharByStrategy(PasswordStrategyType pwdStrategyType, int iRandomLength)
        {
            string vRandStrPwd = string.Empty;
            switch (pwdStrategyType)
            {
                case PasswordStrategyType.UpperCaseLetter:
                    vRandStrPwd = GenerateRandomLetter(iRandomLength);
                    break;
                case PasswordStrategyType.LowerCaseLetter:
                    vRandStrPwd = GenerateRandomLetter(iRandomLength, true);
                    break;
                case PasswordStrategyType.Number:
                    vRandStrPwd = GenerateCheckCodeNum(iRandomLength);
                    break;
                case PasswordStrategyType.SpecialCharacter:
                    vRandStrPwd = GenerateRandomSpecialCharacter(iRandomLength);
                    break;
                default:
                    break;
            }
            return vRandStrPwd;
        }

        /// <summary>
        /// 随机生成字母
        /// </summary>
        /// <param name="iRandomLength">生成长度</param>
        /// <param name="bLower">是否小写字母</param>
        /// <returns></returns>
        private string GenerateRandomLetter(int iRandomLength, bool bLower = false)
        {
            char[] Pattern = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
            if (bLower)
            {
                Pattern = new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
            }

            string result = "";
            int n = Pattern.Length;
            Random random = new Random(~unchecked((int)DateTime.Now.Ticks));
            for (int i = 0; i < iRandomLength; i++)
            {
                int rnd = random.Next(0, n);
                result += Pattern[rnd];
            }
            return result;
        }

        /// <summary>
        /// 随机生成数字
        /// </summary>
        /// <param name="iRandomLength"></param>
        /// <returns></returns>
        private string GenerateCheckCodeNum(int iRandomLength)
        {
            int rep = 0;
            string str = string.Empty;
            long num2 = DateTime.Now.Ticks + rep;
            rep++;
            Random random = new Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> rep)));
            for (int i = 0; i < iRandomLength; i++)
            {
                int num = random.Next();
                str = str + ((char)(0x30 + ((ushort)(num % 10)))).ToString();
            }
            return str;
        }

        /// <summary>
        /// 随机生成特殊字符
        /// </summary>
        /// <param name="iRandomLength">生成长度</param>
        /// <returns></returns>
        private string GenerateRandomSpecialCharacter(int iRandomLength)
        {
            string result = "";
            int n = DefaultSpecialCharacters.Length;
            Random random = new Random(~unchecked((int)DateTime.Now.Ticks));
            for (int i = 0; i < iRandomLength; i++)
            {
                int rnd = random.Next(0, n);
                result += DefaultSpecialCharacters[rnd];
            }
            return result;
        }

        #endregion

        #endregion
    }
}
