using CMSMicroservice.Domain.Events;
using Microsoft.Extensions.Configuration;
using System.Security.Cryptography;
using System.Text;
namespace CMSMicroservice.Application.OtpTokenCQ.Commands.CreateNewOtpToken;
public class CreateNewOtpTokenCommandHandler : IRequestHandler<CreateNewOtpTokenCommand, CreateNewOtpTokenResponseDto>
{
    private readonly IApplicationDbContext _context;
    private readonly IConfiguration _cfg;
    private readonly IHashService _hashService;

    public CreateNewOtpTokenCommandHandler(IApplicationDbContext context, IConfiguration cfg, IHashService hashService)
    {
        _context = context;
        _cfg = cfg;
        _hashService = hashService;
    }

    const int CodeLength = 6;
    const int MaxAttempts = 5;              // محدودیت تلاش    

    static readonly TimeSpan Ttl = TimeSpan.FromMinutes(2);
    static readonly TimeSpan Cooldown = TimeSpan.FromSeconds(60); // فاصله ارسال مجدد
    public async Task<CreateNewOtpTokenResponseDto> Handle(CreateNewOtpTokenCommand request,
        CancellationToken cancellationToken)
    {
        var mobile = request.Mobile.NormalizeIranMobile();
        var purpose = request.Purpose?.ToLowerInvariant() ?? "signup";

        // ریت‌لیمیت ساده: اگر هنوز کدی فعال و تازه داریم، اجازه نده
        var now = DateTime.Now;
        var lastActive = await _context.OtpTokens
            .Where(o => o.Mobile == mobile && o.Purpose == purpose && !o.IsUsed && o.ExpiresAt > now)
            .OrderByDescending(o => o.Created)
            .FirstOrDefaultAsync(cancellationToken);

        if (lastActive is not null && (now - lastActive.Created) < Cooldown)
            return new CreateNewOtpTokenResponseDto()
            {
                Success = false,
                Message = "لطفاً کمی بعد دوباره تلاش کنید."
            };

        // تولید کد
        var code = GenerateNumericCode(CodeLength);
        var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set");
        var codeHash = _hashService.ComputeHmacSha256Hex(code, secret);

        var entity = new OtpToken
        {
            Mobile = mobile,
            Purpose = purpose,
            CodeHash = codeHash,
            ExpiresAt = now.Add(Ttl),
            Attempts = 0,
            IsUsed = false,
            
        };
        await _context.OtpTokens.AddAsync(entity, cancellationToken);
        entity.AddDomainEvent(new CreateNewOtpTokenEvent(entity, code, request.SignGuid));
        await _context.SaveChangesAsync(cancellationToken);
        return new CreateNewOtpTokenResponseDto()
        {
            Success = true,
            Message = "کد ارسال شد.",
            Code = code,
            RemainingAttempts = MaxAttempts,
            RemainingSeconds = Ttl.Seconds
        };
    }

    // --- util‌ها ---
    private string GenerateNumericCode(int len)
    {
        // امن‌تر از Random(): تولید ارقام با RNG
        var bytes = new byte[len];
        RandomNumberGenerator.Fill(bytes);
        var sb = new StringBuilder(len);
        foreach (var b in bytes) sb.Append((b % 10).ToString());
        return sb.ToString();
    }
}
