using CMSMicroservice.Application.Common.Exceptions; using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Application.Common.Models; using CMSMicroservice.Domain.Common; using CMSMicroservice.Domain.Entities; using CMSMicroservice.Domain.Entities.Club; using CMSMicroservice.Domain.Entities.Commission; using CMSMicroservice.Domain.Entities.History; using CMSMicroservice.Domain.Enums; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System.Globalization; namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership; public class ActivateClubMembershipCommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly ICurrentUserService _currentUser; private readonly IWeekDefinitionRepository _weekRepository; private readonly ILogger _logger; public ActivateClubMembershipCommandHandler( IApplicationDbContext context, ICurrentUserService currentUser, IWeekDefinitionRepository weekRepository, ILogger logger) { _context = context; _currentUser = currentUser; _weekRepository = weekRepository; _logger = logger; } public async Task Handle( ActivateClubMembershipCommand request, CancellationToken cancellationToken) { try { _logger.LogInformation( "Activating club membership for UserId: {UserId}", request.UserId ); // 1. بررسی کاربر var user = await _context.Users .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken); if (user == null) { _logger.LogWarning("User not found: {UserId}", request.UserId); throw new NotFoundException(nameof(User), request.UserId); } // 2. بررسی اینکه پکیج خریده باشد if (user.PackagePurchaseMethod == PackagePurchaseMethod.None) { _logger.LogWarning( "User {UserId} has not purchased golden package yet", request.UserId ); throw new BadRequestException( "برای فعالسازی باشگاه مشتریان ابتدا باید پکیج طلایی خریداری کنید" ); } // 3. بررسی موجودی کیف پول var wallet = await _context.UserWallets .FirstOrDefaultAsync(w => w.UserId == user.Id, cancellationToken); if (wallet == null) { _logger.LogError("Wallet not found for UserId: {UserId}", request.UserId); throw new NotFoundException("کیف پول کاربر یافت نشد"); } if (wallet.Balance < SystemConstants.BasePackageAmount) { _logger.LogWarning( "User {UserId} has insufficient balance: {Balance}", request.UserId, wallet.Balance ); throw new BadRequestException( $"برای فعالسازی باشگاه مشتریان باید حداقل {SystemConstants.BasePackageAmount:N0} ریال موجودی اصلی داشته باشید" ); } // 4. پیدا کردن UserOrder با PackageId var packageOrder = await _context.UserOrders .Include(o => o.Transaction) .Where(o => o.UserId == user.Id && o.PackageId != null && o.PaymentStatus == PaymentStatus.Success) .OrderByDescending(o => o.Created) .FirstOrDefaultAsync(cancellationToken); if (packageOrder == null) { _logger.LogWarning( "No successful package order found for UserId: {UserId}", request.UserId ); throw new NotFoundException("سفارش پکیج طلایی یافت نشد"); } // 5. بررسی Transaction if (packageOrder.Transaction == null) { _logger.LogError( "Transaction not found for OrderId: {OrderId}", packageOrder.Id ); throw new NotFoundException("تراکنش مربوط به سفارش یافت نشد"); } var transaction = packageOrder.Transaction; if (transaction.Type != TransactionType.DepositIpg && transaction.Type != TransactionType.DepositExternal1) { _logger.LogWarning( "Invalid transaction type for OrderId {OrderId}: {Type}", packageOrder.Id, transaction.Type ); throw new BadRequestException( "تراکنش معتبر برای فعالسازی باشگاه یافت نشد" ); } // 6. بررسی عضویت فعلی var existingMembership = await _context.ClubMemberships .FirstOrDefaultAsync(c => c.UserId == user.Id, cancellationToken); // 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از SystemConstants long giftValue = SystemConstants.ClubMembershipGiftValue; long activationFeeValue = SystemConstants.ClubActivationFee; _logger.LogInformation( "Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}", giftValue, activationFeeValue ); ClubMembership entity; bool isNewMembership = existingMembership == null; var activationDate = DateTime.Now; if (isNewMembership) { // ایجاد عضویت جدید entity = new ClubMembership { UserId = user.Id, IsActive = true, ActivatedAt = activationDate, InitialContribution =activationFeeValue, GiftValue = giftValue, // مقدار از تنظیمات TotalEarned = 0, PurchaseMethod = user.PackagePurchaseMethod }; _context.ClubMemberships.Add(entity); _logger.LogInformation( "Created new club membership for UserId {UserId} via {Method}, GiftValue: {GiftValue}", user.Id, user.PackagePurchaseMethod, giftValue ); } else { if (existingMembership.IsActive) { _logger.LogInformation( "User {UserId} is already an active club member", user.Id ); return true; } // فعال‌سازی مجدد — ActivatedAt حفظ میشه (overwrite نمیشه) entity = existingMembership; entity.IsActive = true; entity.PurchaseMethod = user.PackagePurchaseMethod; _context.ClubMemberships.Update(entity); _logger.LogInformation( "Reactivated club membership for UserId {UserId}", user.Id ); } await _context.SaveChangesAsync(cancellationToken); // 6.5. ایجاد ClubMembershipCycle — هر خرید پکیج یک دور جدید var previousCycles = await _context.ClubMembershipCycles .Where(c => c.UserId == user.Id && c.IsCurrentCycle) .ToListAsync(cancellationToken); foreach (var prevCycle in previousCycles) { prevCycle.IsCurrentCycle = false; } var maxCycleNumber = await _context.ClubMembershipCycles .Where(c => c.ClubMembershipId == entity.Id) .MaxAsync(c => (int?)c.CycleNumber, cancellationToken) ?? 0; var newCycle = new ClubMembershipCycle { UserId = user.Id, ClubMembershipId = entity.Id, CycleNumber = maxCycleNumber + 1, PackagePurchasedAt = activationDate, PurchaseMethod = user.PackagePurchaseMethod, PackageAmount = SystemConstants.BasePackageAmount, IsCurrentCycle = true }; _context.ClubMembershipCycles.Add(newCycle); await _context.SaveChangesAsync(cancellationToken); _logger.LogInformation( "Created ClubMembershipCycle #{CycleNumber} for UserId {UserId}, MembershipId {MembershipId}", newCycle.CycleNumber, user.Id, entity.Id ); // 7. ثبت تاریخچه var history = new ClubMembershipHistory { ClubMembershipId = entity.Id, UserId = entity.UserId, OldIsActive = !isNewMembership && !existingMembership!.IsActive, NewIsActive = true, Action = ClubMembershipAction.Activated, Reason = isNewMembership ? $"Initial activation via {user.PackagePurchaseMethod}" : $"Reactivated via {user.PackagePurchaseMethod}", PerformedBy = _currentUser.GetPerformedBy() }; _context.ClubMembershipHistories.Add(history); await _context.SaveChangesAsync(cancellationToken); // ⭐ 8. اضافه کردن مبلغ به Pool هفته جاری var currentWeekDefinitionId = GetCurrentWeekDefinitionId(); var weeklyPool = await _context.WeeklyCommissionPools .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken); if (weeklyPool == null) { // ایجاد Pool جدید برای این هفته weeklyPool = new WeeklyCommissionPool { WeekDefinitionId = currentWeekDefinitionId, TotalPoolAmount = activationFeeValue, // مبلغ هدیه به Pool اضافه میشه TotalBalances = 0, // در CalculateWeeklyBalances محاسبه میشه ValuePerBalance = 0, // در CalculateWeeklyCommissionPool محاسبه میشه IsCalculated = false, CalculatedAt = null }; await _context.WeeklyCommissionPools.AddAsync(weeklyPool, cancellationToken); _logger.LogInformation( "Created new WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId} with initial amount: {Amount}", currentWeekDefinitionId, giftValue ); } else { // اضافه کردن به Pool موجود weeklyPool.TotalPoolAmount += activationFeeValue; _context.WeeklyCommissionPools.Update(weeklyPool); _logger.LogInformation( "Added {Amount} to existing WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId}. New total: {NewTotal}", activationFeeValue, currentWeekDefinitionId, weeklyPool.TotalPoolAmount ); } await _context.SaveChangesAsync(cancellationToken); // 9. اضافه کردن ویژگی‌های باشگاه برای کاربر (فقط برای عضویت جدید) if (isNewMembership) { var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds(); var clubFeatures = await _context.ClubFeatures .Where(f => !f.IsDeleted && featureIds.Contains(f.Id)) .ToListAsync(cancellationToken); if (clubFeatures.Any()) { var userClubFeatures = clubFeatures.Select(feature => new UserClubFeature { UserId = user.Id, ClubMembershipId = entity.Id, ClubFeatureId = feature.Id, GrantedAt = activationDate, IsActive = true, Notes = "اعطا شده به‌طور خودکار هنگام فعالسازی" }).ToList(); _context.UserClubFeatures.AddRange(userClubFeatures); await _context.SaveChangesAsync(cancellationToken); _logger.LogInformation( "Granted {Count} club features to UserId {UserId}", clubFeatures.Count, user.Id ); } else { _logger.LogWarning( "No club features found to grant to UserId {UserId}", user.Id ); } } _logger.LogInformation( "Club membership activated successfully. UserId: {UserId}, MembershipId: {MembershipId}", user.Id, entity.Id ); return true; } catch (Exception ex) { _logger.LogError( ex, "Error in ActivateClubMembershipCommand for UserId: {UserId}", request.UserId ); throw; } } /// /// دریافت شناسه تعریف هفته جاری /// private long GetCurrentWeekDefinitionId() { var week = _weekRepository.GetCurrentWeek(); if (week == null) { throw new InvalidOperationException("هفته جاری در سیستم تعریف نشده است"); } return week.Id; } }