namespace Hims.Shared.DataFilters
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    using System.Text.RegularExpressions;

    /// <summary>
    /// The core filter.
    /// </summary>
    public static class CoreFilter
    {
        /// <summary>
        /// The random.
        /// </summary>
        /// <param name="length">
        /// The length.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string Random(int length)
        {
            var random = new Random();
            return new string(Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", length).Select(s => s[random.Next(s.Length)]).ToArray());
        }

        /// <summary>
        /// The random numbers.
        /// </summary>
        /// <param name="length">
        /// The length.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string RandomNumbers(int length)
        {
            var random = new Random();
            return new string(Enumerable.Repeat("0123456789", length).Select(s => s[random.Next(s.Length)]).ToArray());
        }

        /// <summary>
        /// The random.
        /// </summary>
        /// <param name="min">
        /// The min.
        /// </param>
        /// <param name="max">
        /// The max.
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        public static int Random(int min, int max)
        {
            if (min <= 0)
            {
                min = 0;
            }

            if (max <= 0)
            {
                max = 0;
            }

            var randomNumberBuffer = new byte[10];
            new RNGCryptoServiceProvider().GetBytes(randomNumberBuffer);
            return new Random(BitConverter.ToInt32(randomNumberBuffer, 0)).Next(min, max);
        }

        /// <summary>
        /// The full name.
        /// </summary>
        /// <param name="firstName">
        /// The first Name.
        /// </param>
        /// <param name="middleName">
        /// The middle Name.
        /// </param>
        /// <param name="lastName">
        /// The last Name.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string FullName(string firstName, string middleName, string lastName) => firstName + " " + (string.IsNullOrEmpty(middleName) ? string.Empty : middleName + " ") + lastName;

        /// <summary>
        /// The get random password.
        /// </summary>
        /// <param name="length">
        /// The length.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string RandomPassword(int length)
        {
            const int NoOfChunks = 4;
            var finalValue = string.Empty;

            // Initial randoms
            var initialRandoms = new List<int>();
            for (var i = 0; i < NoOfChunks; i++)
            {
                initialRandoms.Add(new Random().Next(1, 3));
            }

            // Padding
            for (; ; )
            {
                if (initialRandoms.Sum() >= length)
                {
                    break;
                }

                var indexRandom = new Random().Next(0, NoOfChunks);
                initialRandoms[indexRandom] = initialRandoms[indexRandom] + 1;
            }

            // Capital Letters
            for (var i = 0; i < initialRandoms[0]; i++)
            {
                var capitalLetterNumber = new Random().Next(65, 91);
                var capitalLetter = Convert.ToChar(capitalLetterNumber);
                finalValue += capitalLetter;
            }

            // Small Letters
            for (var i = 0; i < initialRandoms[1]; i++)
            {
                var smallLetterNumber = new Random().Next(97, 123);
                var smallLetter = Convert.ToChar(smallLetterNumber);
                finalValue += smallLetter;
            }

            // Numbers
            for (var i = 0; i < initialRandoms[2]; i++)
            {
                var number = new Random().Next(0, 9);
                finalValue += number;
            }

            // Special Character
            var randomSpecialCharacters = new List<int>
            {
                33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94,
                95, 96, 123, 124, 125, 126
            };

            for (var i = 0; i < initialRandoms[3]; i++)
            {
                var specialNumber = new Random().Next(0, randomSpecialCharacters.Count);
                var specialLetter = Convert.ToChar(randomSpecialCharacters[specialNumber]);
                finalValue += specialLetter;
            }

            // Jumble
            var jumble = new StringBuilder(finalValue);
            var random = new Random();
            for (var i = jumble.Length - 1; i > 0; i--)
            {
                var j = random.Next(i);
                var temp = jumble[j];
                jumble[j] = jumble[i];
                jumble[i] = temp;
            }

            return jumble.ToString();
        }

        /// <summary>
        /// The to enum.
        /// </summary>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="ignoreCase">
        /// The ignore case.
        /// </param>
        /// <typeparam name="T">
        /// The data type
        /// </typeparam>
        /// <returns>
        /// The enum value.
        /// </returns>
        public static T ToEnum<T>(this string value, bool ignoreCase = true) => (T)Enum.Parse(typeof(T), value, ignoreCase);

        /// <summary>
        /// The to image bytes.
        /// </summary>
        /// <param name="image">
        /// The image.
        /// </param>
        /// <returns>
        /// The <see cref="byte"/>.
        /// </returns>
        public static byte[] ToImageBytes(string image) => !string.IsNullOrEmpty(image) ? Convert.FromBase64String(Regex.Replace(image, "data:image/(png|jpg|PNG|JPG|JPEG|jpeg|gif|GIF|bmp|BMP);base64,", string.Empty)) : null;

        /// <summary>
        /// The to image string.
        /// </summary>
        /// <param name="image">
        /// The image.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string ToImageString(byte[] image) => image == null ? string.Empty : "data:image/png;base64," + Convert.ToBase64String(image);

        /// <summary>
        /// The get transaction id.
        /// </summary>
        /// <param name="transactionId">
        /// The transaction id.
        /// </param>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string GetTransactionId(string transactionId, string type)
        {
            // TYPE20010005
            var year = DateTime.Now.ToString("yy");
            var month = DateTime.Now.ToString("MM");

            if (string.IsNullOrEmpty(transactionId))
            {
                return type + year + month + "0001";
            }

            transactionId = transactionId.Replace(type, string.Empty);
            var lastTxnYear = transactionId.Substring(0, 2);
            var lastTxnMonth = transactionId.Substring(2, 2);
            var counter = Convert.ToInt32(transactionId.Substring(4, 4));

            if (lastTxnYear != year && lastTxnMonth != month)
            {
                return type + year + month + "0001";
            }

            return type + year + month + GetSequenceString(counter + 1);
        }

        /// <summary>
        /// The get ticket number.
        /// </summary>
        /// <param name="previousTicketNum">
        /// The previous ticket num.
        /// </param>
        /// <param name="priority">
        /// The priority.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string GetTicketNumber(string previousTicketNum, string priority)
        {
            // ME0TP20050001
            var randomString = Random(4);
            var year = DateTime.Now.ToString("yy");
            var month = DateTime.Now.ToString("MM");

            if (string.IsNullOrEmpty(previousTicketNum))
            {
                return priority + randomString + year + month + "0001";
            }

            var lastTxnYear = previousTicketNum.Substring(5, 2);
            var lastTxnMonth = previousTicketNum.Substring(7, 2);
            var counter = Convert.ToInt32(previousTicketNum.Substring(9, 4));

            if (lastTxnYear != year && lastTxnMonth != month)
            {
                return priority + randomString + year + month + "0001";
            }

            return priority + randomString + year + month + GetSequenceString(counter + 1);
        }

        /// <summary>
        /// The get ticket number.
        /// </summary>
        /// <param name="firstName">
        /// The previous ticket num.
        /// </param>
        /// <param name="randomString">
        /// The previous ticket num.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string GetReferralCode(string firstName, string randomString)
        {
            // ME0TP20050001
            randomString = randomString != null ? randomString.Substring(4, 4) : "0000";

            if (!string.IsNullOrEmpty(firstName))
            {
                return firstName + GetSequenceString(Convert.ToInt32(randomString) + 1);
            }

            return null;
        }

        /// <summary>
        /// The get sequence string.
        /// </summary>
        /// <param name="num">
        /// The number.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        private static string GetSequenceString(int num)
        {
            if (num < 10)
            {
                return "000" + num;
            }

            if (num < 100)
            {
                return "00" + num;
            }

            if (num < 1000)
            {
                return "0" + num;
            }

            return num.ToString();
        }
    }
}