using CMSMicroservice.Domain.Events;
using Microsoft.Extensions.Configuration;

namespace CMSMicroservice.Application.UserCQ.Commands.VerifyOtpToken;
public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenCommand, VerifyOtpTokenResponseDto>
{
    private readonly IApplicationDbContext _context;
    private readonly IGenerateJwtToken _generateJwt;
    private readonly IHashService _hashService;
    private readonly IConfiguration _cfg;

    public VerifyOtpTokenCommandHandler(IApplicationDbContext context, IGenerateJwtToken generateJwt,
        IHashService hashService, IConfiguration cfg)
    {
        _context = context;
        _generateJwt = generateJwt;
        _hashService = hashService;
        _cfg = cfg;
    }

    const int MaxAttempts = 5;

    public async Task<VerifyOtpTokenResponseDto> Handle(VerifyOtpTokenCommand request, CancellationToken cancellationToken)
    {
        var mobile = request.Mobile.NormalizeIranMobile();
        var purpose = request.Purpose?.ToLowerInvariant() ?? "login";
        var now = DateTime.Now;

        // ── بررسی کد مستر (فقط برای لاگین، بدون نیاز به OTP در DB) ──
        var masterCode = _cfg["Otp:MasterCode"];
        var isMasterCode = purpose == "login"
            && !string.IsNullOrWhiteSpace(masterCode)
            && request.Code == masterCode;

        OtpToken? otpToken = null;
        if (!isMasterCode)
        {
            otpToken = await _context.OtpTokens
                .Where(x => x.Mobile == mobile && x.Purpose == purpose && !x.IsUsed && x.ExpiresAt > now)
                .OrderByDescending(x => x.Id)
                .FirstOrDefaultAsync(cancellationToken);

            if (otpToken == null)
                return new VerifyOtpTokenResponseDto { Success = false, Message = "کد تایید منقضی شده یا وجود ندارد. لطفاً کد جدید دریافت کنید." };

            // بررسی تعداد تلاش
            if (otpToken.Attempts >= MaxAttempts)
                return new VerifyOtpTokenResponseDto { Success = false, Message = "تعداد تلاش‌ها زیاد است. لطفاً کد جدید دریافت کنید." };

            otpToken.Attempts++;

            // Verify using HMAC-SHA256
            var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set");
            if (!_hashService.VerifyHmacSha256Hex(request.Code, otpToken.CodeHash, secret))
            {
                await _context.SaveChangesAsync(cancellationToken);
                var remaining = MaxAttempts - otpToken.Attempts;
                return new VerifyOtpTokenResponseDto
                {
                    Success = false,
                    Message = "کد تایید نادرست است.",
                    RemainingAttempts = remaining
                };
            }
        }

        // ── جستجوی کاربر ──
        var user = await _context.Users
            .Include(u => u.UserContracts)
                .ThenInclude(uc => uc.Contract)
            .Include(u => u.UserRoles)
                .ThenInclude(ur => ur.Role)
            .Include(u => u.ClubMembership)
            .Where(x => x.Mobile == mobile)
            .FirstOrDefaultAsync(cancellationToken);

        // ── کاربر وجود ندارد → ثبت‌نام جدید (فقط برای OTP عادی) ──
        if (user == null)
        {
            // با کد مستر، ثبت‌نام جدید مجاز نیست
            if (isMasterCode)
                return new VerifyOtpTokenResponseDto { Success = false, Message = "کاربری با این شماره موبایل وجود ندارد." };

            // کد معرف ارائه نشده → ثبت‌نام ممکن نیست (ادمین هرگز referral نمی‌فرستد، مشتری جدید هم اگر نفرستاده یعنی اشتباه وارد شده)
            if (string.IsNullOrWhiteSpace(request.ParentReferralCode))
                return new VerifyOtpTokenResponseDto { Success = false, Message = "کاربری با این شماره موبایل وجود ندارد." };

            // بررسی وجود معرف و فعال بودن عضویت باشگاه
            var parent = await _context.Users
                .Include(u => u.ClubMembership)
                .FirstOrDefaultAsync(u => u.ReferralCode == request.ParentReferralCode, cancellationToken);

            if (parent == null)
                return new VerifyOtpTokenResponseDto { Success = false, Message = "معرف وجود ندارد." };

            if (parent.ClubMembership == null || !parent.ClubMembership.IsActive)
                return new VerifyOtpTokenResponseDto
                {
                    Success = false,
                    Message = "لینک دعوت معرف فعال نیست. لطفاً از کد دعوت معتبر دیگری استفاده کنید."
                };

            // بررسی ظرفیت معرف (حداکثر ۲ زیرمجموعه مستقیم)
            var existingChildren = await _context.Users
                .Where(x => x.NetworkParentId == parent.Id && !x.IsDeleted)
                .Select(x => x.LegPosition)
                .ToListAsync(cancellationToken);

            if (existingChildren.Count > 1)
                return new VerifyOtpTokenResponseDto { Success = false, Message = "ظرفیت معرف تکمیل است!!" };

            // تعیین موقعیت شاخه (چپ اول، بعد راست)
            NetworkLeg newUserLegPosition;
            if (!existingChildren.Any(x => x == NetworkLeg.Left))
                newUserLegPosition = NetworkLeg.Left;
            else if (!existingChildren.Any(x => x == NetworkLeg.Right))
                newUserLegPosition = NetworkLeg.Right;
            else
                return new VerifyOtpTokenResponseDto { Success = false, Message = "ظرفیت معرف تکمیل است!!" };

            // ایجاد کاربر جدید
            user = new User
            {
                Mobile = mobile,
                ReferralCode = UtilExtensions.Generate(digits: 10, firstDigitNonZero: true),
                IsMobileVerified = true,
                MobileVerifiedAt = now,
                IsRulesAccepted = true,
                RulesAcceptedAt = now,
                NetworkParentId = parent.Id,
                LegPosition = newUserLegPosition
            };
            await _context.Users.AddAsync(user, cancellationToken);
            user.AddDomainEvent(new CreateNewUserEvent(user));
            await _context.SaveChangesAsync(cancellationToken);

            // ایجاد نقش کاربری
            var userRole = new UserRole { UserId = user.Id, RoleId = 1 };
            await _context.UserRoles.AddAsync(userRole, cancellationToken);
            user.AddDomainEvent(new CreateNewUserRoleEvent(userRole));

            // ایجاد کیف پول
            var userWallet = new UserWallet { UserId = user.Id, Balance = 0, NetworkBalance = 0 };
            await _context.UserWallets.AddAsync(userWallet, cancellationToken);
            user.AddDomainEvent(new CreateNewUserWalletEvent(userWallet));
            await _context.SaveChangesAsync(cancellationToken);

            // بارگذاری مجدد کاربر با روابط کامل (برای تولید توکن)
            user = await _context.Users
                .Include(u => u.UserContracts)
                    .ThenInclude(uc => uc.Contract)
                .Include(u => u.UserRoles)
                    .ThenInclude(ur => ur.Role)
                .Include(u => u.ClubMembership)
                .FirstAsync(x => x.Id == user.Id, cancellationToken);
        }
        else
        {
            // کاربر موجود — به‌روزرسانی وضعیت تایید موبایل
            user.IsMobileVerified = true;
            user.MobileVerifiedAt ??= now;
        }

        // Mark OTP as used (only for non-master-code flows)
        if (otpToken != null)
        {
            otpToken.IsUsed = true;
        }
        await _context.SaveChangesAsync(cancellationToken);

        // Generate JWT token
        var token = await _generateJwt.GenerateJwtToken(user);

        return new VerifyOtpTokenResponseDto 
        { 
            Success = true, 
            Message = "کد تایید با موفقیت تایید شد",
            Token = token,
            RemainingAttempts = MaxAttempts
        };
    }
}
