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


namespace CryptoProvider {

  public static class AesService {

    public static IAesKey GenerateKey() {
      using var aes = Aes.Create();

      aes.GenerateIV();
      aes.GenerateKey();

      return new AesKey {
        IV = Convert.ToBase64String(aes.IV),
        Key = Convert.ToBase64String(aes.Key)
      };
    }

    public static string EncryptString(string key, string plainText) =>
      EncryptStringCore(new byte[16], Convert.FromBase64String(key), plainText);

    public static string EncryptString(string iv, string key, string plainText) =>
      EncryptStringCore(Convert.FromBase64String(iv), Convert.FromBase64String(key), plainText);

    public static string DecryptString(string key, string cipherText) =>
      DecryptStringCore(new byte[16], Convert.FromBase64String(key), cipherText);

    public static string DecryptString(string iv, string key, string cipherText) =>
      DecryptStringCore(Convert.FromBase64String(iv), Convert.FromBase64String(key), cipherText);

    #region Core methods
    private static string EncryptStringCore(byte[] iv, byte[] key, string plainText) {
      byte[] array;

      using var aes = Aes.Create();

      aes.Key = key;
      aes.IV = iv;

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

      using var memoryStream = new MemoryStream();
      using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
      using var streamWriter = new StreamWriter(cryptoStream);

      streamWriter.Write(plainText);
      streamWriter.Flush();
      cryptoStream.FlushFinalBlock();

      array = memoryStream.ToArray();

      return Convert.ToBase64String(array);
    }

    private static string DecryptStringCore(byte [] iv, byte [] key, string cipherText) {
      byte[] buffer = Convert.FromBase64String(cipherText);

      using var aes = Aes.Create();


      aes.Key = key;
      aes.IV = iv;

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

      using var memoryStream = new MemoryStream(buffer);
      using var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
      using var streamReader = new StreamReader(cryptoStream);
        
      return streamReader.ReadToEnd();
            
    }
    #endregion
  }
}
