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

namespace File_Folder_Helper.Helpers;

public static class RijndaelEncryption
{ // cSpell:disable

    /// <summary>
    /// Change the input key GUID when you use this code in your own program.
    /// Keep this input key 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 password salt</param>
    /// <returns>The encrypted text</returns>
    public static string Encrypt(string text, string salt)
    {
        string result;
        if (string.IsNullOrEmpty(text))
            throw new ArgumentNullException(nameof(text));
#pragma warning disable SYSLIB0022
        RijndaelManaged aesAlg = NewRijndaelManaged(salt);
#pragma warning restore
        ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
        MemoryStream msEncrypt = new();
        using (CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write))
        using (StreamWriter swEncrypt = new(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();
#pragma warning restore
        result = (base64String.Length % 4 == 0) && Regex.IsMatch(base64String, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);
#pragma warning restore
        return result;
    }

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

    /// <summary>
    /// Create a new RijndaelManaged class and initialize it
    /// </summary>
    /// <param name="salt">The password salt</param>
    /// <returns></returns>
#pragma warning disable SYSLIB0022, SYSLIB0041, CA5379
    private static RijndaelManaged NewRijndaelManaged(string salt)
    {
        ArgumentNullException.ThrowIfNull(salt);
        byte[] saltBytes = Encoding.ASCII.GetBytes(salt);
        Rfc2898DeriveBytes key = new(_InputKey, saltBytes);
        RijndaelManaged aesAlg = new();
#pragma warning restore
        aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
        aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
        return aesAlg;
    }

}