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

namespace Managing.Core;

/// <summary>
/// Provides AES-256-CBC encryption utilities with HMAC-SHA256 authentication for secure token generation.
/// </summary>
public static class CryptoHelpers
{
    /// <summary>
    /// Encrypts a string using AES-256-CBC encryption with HMAC-SHA256 authentication.
    /// </summary>
    /// <param name="plaintext">The text to encrypt</param>
    /// <param name="secretKey">The secret key for encryption (should be 32 bytes for AES-256)</param>
    /// <returns>Base64 encoded encrypted data with IV and HMAC</returns>
    public static string EncryptAesCbc(string plaintext, string secretKey)
    {
        if (string.IsNullOrEmpty(plaintext))
            throw new ArgumentException("Plaintext cannot be null or empty", nameof(plaintext));
        
        if (string.IsNullOrEmpty(secretKey))
            throw new ArgumentException("Secret key cannot be null or empty", nameof(secretKey));

        // Convert secret key to bytes (ensure it's 32 bytes for AES-256)
        var keyBytes = Encoding.UTF8.GetBytes(secretKey);
        if (keyBytes.Length != 32)
        {
            // If key is not 32 bytes, hash it to get 32 bytes
            using var sha256 = SHA256.Create();
            keyBytes = sha256.ComputeHash(keyBytes);
        }

        // Convert plaintext to bytes
        var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);

        // Generate a random IV (16 bytes for AES)
        var iv = new byte[16];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(iv);
        }

        // Encrypt using AES-CBC
        using var aes = Aes.Create();
        aes.Key = keyBytes;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        aes.IV = iv;

        using var encryptor = aes.CreateEncryptor();
        var ciphertext = encryptor.TransformFinalBlock(plaintextBytes, 0, plaintextBytes.Length);

        // Create HMAC for authentication
        using var hmac = new HMACSHA256(keyBytes);
        var hmacBytes = hmac.ComputeHash(ciphertext);

        // Combine IV + encrypted data + HMAC
        var result = new byte[iv.Length + ciphertext.Length + hmacBytes.Length];
        Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
        Buffer.BlockCopy(ciphertext, 0, result, iv.Length, ciphertext.Length);
        Buffer.BlockCopy(hmacBytes, 0, result, iv.Length + ciphertext.Length, hmacBytes.Length);

        return Convert.ToBase64String(result);
    }

    /// <summary>
    /// Decrypts a string using AES-256-CBC decryption with HMAC-SHA256 authentication.
    /// </summary>
    /// <param name="encryptedData">Base64 encoded encrypted data with IV and HMAC</param>
    /// <param name="secretKey">The secret key for decryption (should be 32 bytes for AES-256)</param>
    /// <returns>The decrypted plaintext</returns>
    public static string DecryptAesCbc(string encryptedData, string secretKey)
    {
        if (string.IsNullOrEmpty(encryptedData))
            throw new ArgumentException("Encrypted data cannot be null or empty", nameof(encryptedData));
        
        if (string.IsNullOrEmpty(secretKey))
            throw new ArgumentException("Secret key cannot be null or empty", nameof(secretKey));

        // Convert secret key to bytes (ensure it's 32 bytes for AES-256)
        var keyBytes = Encoding.UTF8.GetBytes(secretKey);
        if (keyBytes.Length != 32)
        {
            // If key is not 32 bytes, hash it to get 32 bytes
            using var sha256 = SHA256.Create();
            keyBytes = sha256.ComputeHash(keyBytes);
        }

        // Decode the base64 data
        var encryptedBytes = Convert.FromBase64String(encryptedData);

        // Extract IV (first 16 bytes), HMAC (last 32 bytes), and ciphertext (middle)
        if (encryptedBytes.Length < 48) // 16 (IV) + 32 (HMAC) = minimum 48 bytes
            throw new ArgumentException("Encrypted data is too short", nameof(encryptedData));

        var iv = new byte[16];
        var hmacBytes = new byte[32];
        var ciphertext = new byte[encryptedBytes.Length - 48];

        Buffer.BlockCopy(encryptedBytes, 0, iv, 0, 16);
        Buffer.BlockCopy(encryptedBytes, encryptedBytes.Length - 32, hmacBytes, 0, 32);
        Buffer.BlockCopy(encryptedBytes, 16, ciphertext, 0, ciphertext.Length);

        // Verify HMAC for authentication
        using var hmac = new HMACSHA256(keyBytes);
        var computedHmac = hmac.ComputeHash(ciphertext);
        
        if (!computedHmac.SequenceEqual(hmacBytes))
        {
            throw new CryptographicException("HMAC verification failed - data may have been tampered with");
        }

        // Decrypt using AES-CBC
        using var aes = Aes.Create();
        aes.Key = keyBytes;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        aes.IV = iv;

        using var decryptor = aes.CreateDecryptor();
        var decryptedBytes = decryptor.TransformFinalBlock(ciphertext, 0, ciphertext.Length);
        return Encoding.UTF8.GetString(decryptedBytes);
    }

    /// <summary>
    /// Encrypts a string using AES-256-CBC encryption (alias for EncryptAesCbc for backward compatibility).
    /// </summary>
    /// <param name="plaintext">The text to encrypt</param>
    /// <param name="secretKey">The secret key for encryption</param>
    /// <returns>Base64 encoded encrypted data</returns>
    public static string EncryptAesGcm(string plaintext, string secretKey)
    {
        return EncryptAesCbc(plaintext, secretKey);
    }

    /// <summary>
    /// Decrypts a string using AES-256-CBC decryption (alias for DecryptAesCbc for backward compatibility).
    /// </summary>
    /// <param name="encryptedData">Base64 encoded encrypted data</param>
    /// <param name="secretKey">The secret key for decryption</param>
    /// <returns>The decrypted plaintext</returns>
    public static string DecryptAesGcm(string encryptedData, string secretKey)
    {
        return DecryptAesCbc(encryptedData, secretKey);
    }
} 