using DigitalData.UserManager.Application.Services.Options;
using Microsoft.Extensions.Options;
using System.Security.Cryptography;
using System.Text;

namespace DigitalData.UserManager.Application.Services
{
    public class Encryptor
    {
        public const int KeyByteLength = 32;

        private readonly EncryptionParameters _params;

        public Encryptor(IOptions<EncryptionParameters> options)
        {
            _params = options.Value;
        }

        public string Encrypt(string plainText) => Encrypt(plainText, _params.Key, _params.IV);

        public string Decrypt(string cipherText) => Decrypt(cipherText, _params.Key, _params.IV);

        public static string Encrypt(string plainText, string key, string iv)
        {
            using Aes aes = Aes.Create();
            aes.KeySize = KeyByteLength * 8;
            aes.Key = AdjustKeySize(Encoding.UTF8.GetBytes(key), aes.KeySize / 8);
            aes.IV = AdjustKeySize(Encoding.UTF8.GetBytes(iv), aes.BlockSize / 8);

            ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

            using MemoryStream ms = new();
            using (CryptoStream cs = new(ms, encryptor, CryptoStreamMode.Write))
            {
                using StreamWriter sw = new(cs);
                sw.Write(plainText);
            }
            return Convert.ToBase64String(ms.ToArray());
        }

        public static string Decrypt(string cipherText, string key, string iv)
        {
            using Aes aes = Aes.Create();
            aes.KeySize = KeyByteLength * 8;
            aes.Key = AdjustKeySize(Encoding.UTF8.GetBytes(key), aes.KeySize / 8);
            aes.IV = AdjustKeySize(Encoding.UTF8.GetBytes(iv), aes.BlockSize / 8);

            ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

            using MemoryStream ms = new(Convert.FromBase64String(cipherText));
            using CryptoStream cs = new(ms, decryptor, CryptoStreamMode.Read);
            using StreamReader sr = new(cs);
            return sr.ReadToEnd();
        }

        public static EncryptionParameters GenerateParameters()
        {
            using Aes aes = Aes.Create();
            aes.KeySize = KeyByteLength * 8;
            aes.GenerateKey();
            aes.GenerateIV();
            return new EncryptionParameters
            {
                Key = Convert.ToBase64String(aes.Key),
                IV = Convert.ToBase64String(aes.IV)
            };
        }

        private static byte[] AdjustKeySize(byte[] key, int size)
        {
            if (key.Length < size)
                Array.Resize(ref key, size);
            else if (key.Length > size)
                Array.Resize(ref key, size);
            return key;
        }
    }
}