namespace Hims.Infrastructure.Helpers
{
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web;
    using Domain.Configurations;
    using Domain.Helpers;

    /// <inheritdoc />
    public class AESHelper : IAESHelper
    {
        /// <summary>
        /// The key vault configuration.
        /// </summary>
        private readonly IKeyVaultConfiguration keyVaultConfiguration;

        /// <summary>
        /// Initializes a new instance of the <see cref="AESHelper"/> class.
        /// </summary>
        /// <param name="keyVaultConfiguration">
        /// The key vault configuration.
        /// </param>
        public AESHelper(IKeyVaultConfiguration keyVaultConfiguration) => this.keyVaultConfiguration = keyVaultConfiguration;

        /// <inheritdoc />
        public string Decode(string text)
        {
            if (string.IsNullOrEmpty(text)) return null;

            try
            {
                var input = text;

                if (input.EndsWith("%3D%3D"))
                {
                    input = input.Replace("%3D%3D", "==");
                }

                else if (input.EndsWith("%3D"))
                {
                    input = input.Replace("%3D", "=");
                }

                else if (input.EndsWith("%253D%253D"))
                {
                    input = input.Replace("%253D%253D", "==");
                }

                else if (input.EndsWith("%253D"))
                {
                    input = input.Replace("%253D", "=");
                }

                else if (input.EndsWith("%25253D"))
                {
                    input = input.Replace("%25253D", "=");
                }
                else if (!input.EndsWith("="))
                {
                    if (input.Length % 4 == 0)
                    {
                        input = input + "";
                    }
                    else if (input.Length % 4 == 1)
                    {
                        input = input + "===";
                    }
                    else if (input.Length % 4 == 2)
                    {
                        input = input + "==";
                    }
                    else if (input.Length % 4 == 3)
                    {
                        input = input + "=";
                    }
                }

                var bytes = HttpUtility.UrlDecodeToBytes(Convert.FromBase64String(input));
                return Encoding.ASCII.GetString(bytes);
            }
            catch
            {
                var bytes = HttpUtility.UrlDecodeToBytes(Convert.FromBase64String(text));
                return Encoding.ASCII.GetString(bytes);
            }
        }

        /// <inheritdoc />
        public string Encode(string text)
        {
            var bytes = HttpUtility.UrlEncodeToBytes(Encoding.ASCII.GetBytes(text));
            var res = Convert.ToBase64String(bytes);
            if (res.EndsWith("=="))
            {
                res = res.Replace("==", "%3D%3D");
            }

            if (res.EndsWith("="))
            {
                res = res.Replace("=", "%3D");
            }

            return res;
        }

        /// <inheritdoc />
        public string Decrypt(string text)
        {
            text = Regex.Replace(text, @"\s", "+");
            string plaintext;
            using (var rijAlg = new RijndaelManaged())
            {
                rijAlg.Mode = CipherMode.CBC;
                rijAlg.Padding = PaddingMode.PKCS7;
                rijAlg.FeedbackSize = 128;
                rijAlg.Key = Encoding.UTF8.GetBytes(this.keyVaultConfiguration.AESKey);
                rijAlg.IV = Encoding.UTF8.GetBytes(this.keyVaultConfiguration.AESKey);

                var decrypt = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
                using (var msdDecrypt = new MemoryStream(Convert.FromBase64String(text)))
                {
                    using (var csdDecrypt = new CryptoStream(msdDecrypt, decrypt, CryptoStreamMode.Read))
                    {
                        using (var srdDecrypt = new StreamReader(csdDecrypt))
                        {
                            plaintext = srdDecrypt.ReadToEnd();
                        }
                    }
                }
            }

            return plaintext;
        }

        /// <inheritdoc />
        public string Encrypt(string text)
        {
            byte[] encrypted;
            using (var rijAlg = new RijndaelManaged())
            {
                rijAlg.Mode = CipherMode.CBC;
                rijAlg.Padding = PaddingMode.PKCS7;
                rijAlg.FeedbackSize = 128;
                rijAlg.Key = Encoding.UTF8.GetBytes(this.keyVaultConfiguration.AESKey);
                rijAlg.IV = Encoding.UTF8.GetBytes(this.keyVaultConfiguration.AESKey);

                var encryption = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
                using (var msdEncrypt = new MemoryStream())
                {
                    using (var csdEncrypt = new CryptoStream(msdEncrypt, encryption, CryptoStreamMode.Write))
                    {
                        using (var swdEncrypt = new StreamWriter(csdEncrypt))
                        {
                            swdEncrypt.Write(text);
                        }

                        encrypted = msdEncrypt.ToArray();
                    }
                }
            }

            return Convert.ToBase64String(encrypted);
        }
    }
}