using System.Security.Cryptography;
using System.Text;
using CMSMicroservice.Application.Common.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using CMSMicroservice.Domain.Entities;

namespace CMSMicroservice.Application.UserCQ.Commands.CreateNewOtpToken;

public class CreateNewOtpTokenCommandHandler : IRequestHandler<CreateNewOtpTokenCommand, CreateNewOtpTokenResponseDto>
{
    private readonly IApplicationDbContext _context;
    private readonly IKavenegarService _kavenegarService;
    private readonly IHashService _hashService;
    private readonly IConfiguration _cfg;

    const int CodeLength = 6;
    const int MaxAttempts = 5;
    static readonly TimeSpan Ttl = TimeSpan.FromMinutes(2);
    static readonly TimeSpan Cooldown = TimeSpan.FromSeconds(60);

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

    public async Task<CreateNewOtpTokenResponseDto> Handle(CreateNewOtpTokenCommand request,
        CancellationToken cancellationToken)
    {
        var mobile = request.Mobile.NormalizeIranMobile();
        var purpose = request.Purpose?.ToLowerInvariant() ?? "login";
        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);

        // Create new OTP token
        var otpToken = new OtpToken
        {
            Mobile = mobile,
            Purpose = purpose,
            CodeHash = codeHash,
            IsUsed = false,
            Attempts = 0,
            ExpiresAt = now.Add(Ttl)
        };

        _context.OtpTokens.Add(otpToken);
        await _context.SaveChangesAsync(cancellationToken);

        try
        {
            // برای امضای قرارداد، از الگوی Sign-Contract با GUID به عنوان token2 استفاده شود
            if ((purpose == "signcontract" || purpose == "signclubcontract") && !string.IsNullOrEmpty(request.SignGuid))
            {
                await _kavenegarService.VerifyLookupAsync(mobile, code, request.SignGuid, "Sign-Contract");
            }
            else
            {
                await _kavenegarService.VerifyLookupAsync(mobile, code);
            }
        }
        catch (Exception)
        {
            // Log error but don't fail the request
        }

        return new CreateNewOtpTokenResponseDto
        {
            Success = true,
            Message = "کد تایید با موفقیت ارسال شد",
            RemainingAttempts = MaxAttempts,
            RemainingSeconds = (int)Ttl.TotalSeconds,
            ExpiresAt = otpToken.ExpiresAt
        };
    }

    private static string GenerateNumericCode(int len)
    {
        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();
    }
}