using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace WhatsAppService.Infrastructure.Security;

/// <summary>
/// EN: Interface for encrypting/decrypting WhatsApp access tokens.
/// VI: Interface để mã hóa/giải mã access token WhatsApp.
/// </summary>
public interface IAccessTokenEncryptionService
{
    /// <summary>
    /// EN: Encrypt plain access token.
    /// VI: Mã hóa access token dạng plain text.
    /// </summary>
    string Encrypt(string plainTextToken);

    /// <summary>
    /// EN: Decrypt encrypted access token.
    /// VI: Giải mã access token đã mã hóa.
    /// </summary>
    string Decrypt(string encryptedToken);
}

/// <summary>
/// EN: AES-GCM token encryption service.
/// VI: Service mã hóa token sử dụng AES-GCM.
/// </summary>
public class AccessTokenEncryptionService : IAccessTokenEncryptionService
{
    private const string TokenPrefix = "v1:";
    private readonly byte[] _keyBytes;
    private readonly ILogger<AccessTokenEncryptionService> _logger;

    public AccessTokenEncryptionService(
        IConfiguration configuration,
        ILogger<AccessTokenEncryptionService> logger)
    {
        _logger = logger;
        var rawKey = configuration["Security:AccessTokenEncryptionKey"]
            ?? configuration["ACCESS_TOKEN_ENCRYPTION_KEY"]
            ?? throw new InvalidOperationException(
                "Security:AccessTokenEncryptionKey (or ACCESS_TOKEN_ENCRYPTION_KEY) is required.");

        _keyBytes = SHA256.HashData(Encoding.UTF8.GetBytes(rawKey));
    }

    public string Encrypt(string plainTextToken)
    {
        if (string.IsNullOrWhiteSpace(plainTextToken))
            throw new ArgumentException("Token cannot be empty.", nameof(plainTextToken));

        var nonce = RandomNumberGenerator.GetBytes(12);
        var plainBytes = Encoding.UTF8.GetBytes(plainTextToken);
        var cipherBytes = new byte[plainBytes.Length];
        var tagBytes = new byte[16];

        using var aes = new AesGcm(_keyBytes, 16);
        aes.Encrypt(nonce, plainBytes, cipherBytes, tagBytes);

        var payload = new byte[nonce.Length + tagBytes.Length + cipherBytes.Length];
        Buffer.BlockCopy(nonce, 0, payload, 0, nonce.Length);
        Buffer.BlockCopy(tagBytes, 0, payload, nonce.Length, tagBytes.Length);
        Buffer.BlockCopy(cipherBytes, 0, payload, nonce.Length + tagBytes.Length, cipherBytes.Length);

        return $"{TokenPrefix}{Convert.ToBase64String(payload)}";
    }

    public string Decrypt(string encryptedToken)
    {
        if (string.IsNullOrWhiteSpace(encryptedToken))
            throw new ArgumentException("Encrypted token cannot be empty.", nameof(encryptedToken));

        // EN: Backward compatibility for legacy plain-text records.
        // VI: Tương thích ngược với bản ghi legacy lưu plain-text.
        if (!encryptedToken.StartsWith(TokenPrefix, StringComparison.Ordinal))
        {
            _logger.LogWarning(
                "Decrypt called with legacy plain token format. Consider re-encrypting stored access token.");
            return encryptedToken;
        }

        var base64 = encryptedToken[TokenPrefix.Length..];
        var payload = Convert.FromBase64String(base64);
        if (payload.Length <= 28)
            throw new InvalidOperationException("Encrypted token payload is invalid.");

        var nonce = payload[..12];
        var tag = payload[12..28];
        var cipherBytes = payload[28..];
        var plainBytes = new byte[cipherBytes.Length];

        using var aes = new AesGcm(_keyBytes, 16);
        aes.Decrypt(nonce, cipherBytes, tag, plainBytes);

        return Encoding.UTF8.GetString(plainBytes);
    }
}
