using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace Shared
{

    public static class RijndaelEncryption
    {
        /// <summary>
        /// Change the Inputkey GUID when you use this code in your own program.
        /// Keep this inputkey very safe and prevent someone from decoding it some way!!
        /// Generated 2021-08-10
        /// </summary>
        internal const string Inputkey = "970CCEF6-4307-4F6A-9AC8-377DADB889BD";

        /// <summary>
        /// Encrypt the given text and give the byte array back as a BASE64 string
        /// </summary>
        /// <param name="text">The text to encrypt</param>
        /// <param name="salt">The pasword salt</param>
        /// <returns>The encrypted text</returns>
        public static string Encrypt(string text, string salt)
        {
            string result;
            if (string.IsNullOrEmpty(text))
                throw new ArgumentNullException("text");
            RijndaelManaged aesAlg = NewRijndaelManaged(salt);
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
            MemoryStream msEncrypt = new MemoryStream();
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                swEncrypt.Write(text);
            result = Convert.ToBase64String(msEncrypt.ToArray());
            return result;
        }

        /// <summary>
        /// Checks if a string is base64 encoded
        /// </summary>
        /// <param name="base64String">The base64 encoded string</param>
        /// <returns></returns>
        public static bool IsBase64String(string base64String)
        {
            bool result;
            base64String = base64String.Trim();
            result = (base64String.Length % 4 == 0) && Regex.IsMatch(base64String, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);
            return result;
        }

        /// <summary>
        /// Decrypts the given text
        /// </summary>
        /// <param name="cipherText">The encrypted BASE64 text</param>
        /// <param name="salt">The pasword salt</param>
        /// <returns>De gedecrypte text</returns>
        public static string Decrypt(string cipherText, string salt)
        {
            if (string.IsNullOrEmpty(cipherText))
                throw new ArgumentNullException("cipherText");
            if (!IsBase64String(cipherText))
                throw new Exception("The cipherText input parameter is not base64 encoded");
            string text;
            RijndaelManaged aesAlg = NewRijndaelManaged(salt);
            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
            byte[] cipher = Convert.FromBase64String(cipherText);
            using (MemoryStream msDecrypt = new MemoryStream(cipher))
            {
                using CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
                using StreamReader srDecrypt = new StreamReader(csDecrypt);
                text = srDecrypt.ReadToEnd();
            }
            return text;
        }

        /// <summary>
        /// Create a new RijndaelManaged class and initialize it
        /// </summary>
        /// <param name="salt">The pasword salt</param>
        /// <returns></returns>
        private static RijndaelManaged NewRijndaelManaged(string salt)
        {
            if (salt == null)
                throw new ArgumentNullException("salt");
            byte[] saltBytes = Encoding.ASCII.GetBytes(salt);
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(Inputkey, saltBytes);
            RijndaelManaged aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
            aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
            return aesAlg;
        }
    }

}