// EN: TOTP-based two-factor authentication service
// VI: Service xác thực hai yếu tố dựa trên TOTP

using System.Security.Cryptography;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OtpNet;
using QRCoder;

namespace IamService.Infrastructure.TwoFactor;

/// <summary>
/// EN: TOTP-based two-factor authentication service implementation.
/// VI: Implementation service 2FA dựa trên TOTP.
/// </summary>
public class TotpTwoFactorService : ITwoFactorService
{
    private readonly TwoFactorSettings _settings;
    private readonly ILogger<TotpTwoFactorService> _logger;

    public TotpTwoFactorService(
        IOptions<TwoFactorSettings> settings,
        ILogger<TotpTwoFactorService> logger)
    {
        _settings = settings.Value;
        _logger = logger;
    }

    /// <inheritdoc />
    public string GenerateSecretKey()
    {
        // EN: Generate 20 bytes (160 bits) secret key for TOTP
        // VI: Tạo secret key 20 bytes (160 bits) cho TOTP
        var key = KeyGeneration.GenerateRandomKey(20);
        return Base32Encoding.ToString(key);
    }

    /// <inheritdoc />
    public string GenerateQrCodeUri(string email, string secretKey)
    {
        // EN: Format: otpauth://totp/{issuer}:{email}?secret={secret}&issuer={issuer}
        // VI: Format: otpauth://totp/{issuer}:{email}?secret={secret}&issuer={issuer}
        var issuer = Uri.EscapeDataString(_settings.Issuer);
        var accountName = Uri.EscapeDataString(email);
        
        return $"otpauth://totp/{issuer}:{accountName}?secret={secretKey}&issuer={issuer}&digits={_settings.CodeLength}&period={_settings.ValidityPeriodSeconds}";
    }

    /// <inheritdoc />
    public string GenerateQrCodeBase64(string email, string secretKey)
    {
        try
        {
            var uri = GenerateQrCodeUri(email, secretKey);
            
            using var qrGenerator = new QRCodeGenerator();
            var qrCodeData = qrGenerator.CreateQrCode(uri, QRCodeGenerator.ECCLevel.Q);
            
            using var qrCode = new PngByteQRCode(qrCodeData);
            var pngBytes = qrCode.GetGraphic(10);
            
            return $"data:image/png;base64,{Convert.ToBase64String(pngBytes)}";
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to generate QR code for {Email}", email);
            throw;
        }
    }

    /// <inheritdoc />
    public bool ValidateCode(string secretKey, string code)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(secretKey) || string.IsNullOrWhiteSpace(code))
            {
                return false;
            }

            var secretBytes = Base32Encoding.ToBytes(secretKey);
            var totp = new Totp(secretBytes, step: _settings.ValidityPeriodSeconds);
            
            // EN: Allow 1 step tolerance (previous and next period)
            // VI: Cho phép dung sai 1 bước (kỳ trước và sau)
            var isValid = totp.VerifyTotp(code, out _, new VerificationWindow(previous: 1, future: 1));
            
            _logger.LogDebug("TOTP validation result: {IsValid}", isValid);
            return isValid;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to validate TOTP code");
            return false;
        }
    }

    /// <inheritdoc />
    public IEnumerable<string> GenerateRecoveryCodes(int count = 10)
    {
        var codes = new List<string>();
        
        for (int i = 0; i < count; i++)
        {
            // EN: Generate 12 bytes to ensure enough characters after base64 conversion
            // VI: Tạo 12 bytes để đảm bảo đủ ký tự sau khi chuyển đổi base64
            var bytes = RandomNumberGenerator.GetBytes(12);
            var code = Convert.ToBase64String(bytes)
                .Replace("+", "X")
                .Replace("/", "Y")
                .Replace("=", "")
                .ToUpperInvariant();
            
            // EN: Take first 8 characters, ensure we have enough
            // VI: Lấy 8 ký tự đầu tiên, đảm bảo có đủ
            if (code.Length >= 8)
            {
                // EN: Format as XXXX-XXXX for readability
                // VI: Format thành XXXX-XXXX để dễ đọc
                codes.Add($"{code.Substring(0, 4)}-{code.Substring(4, 4)}");
            }
            else
            {
                // EN: Fallback - pad if necessary
                // VI: Fallback - pad nếu cần
                code = code.PadRight(8, 'Z');
                codes.Add($"{code.Substring(0, 4)}-{code.Substring(4, 4)}");
            }
        }
        
        return codes;
    }
}
