using FirebaseAdmin.Messaging; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Entitlements; using Marco.Pms.Services.Service.ServiceInterfaces; using MarcoBMS.Services.Service; using Microsoft.EntityFrameworkCore; namespace Marco.Pms.Services.Service { public class FirebaseService : IFirebaseService { private readonly IDbContextFactory _dbContextFactory; private readonly ILoggingService _logger; public FirebaseService(IDbContextFactory dbContextFactory, ILoggingService logger) { _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task SendDemoMessages(Notification notificationFirebase) { string deviceToken = ""; var message = new Message() { Token = deviceToken, Notification = notificationFirebase }; string response = await FirebaseMessaging.DefaultInstance.SendAsync(message); } public async Task SendLoginMessageAsync(Notification notificationFirebase) { await using var _context = await _dbContextFactory.CreateDbContextAsync(); // List of device registration tokens to send the message to var registrationTokens = await _context.FCMTokenMappings.Select(ft => ft.FcmToken).ToListAsync(); await SendMessageToMultipleDevicesAsync(registrationTokens, notificationFirebase); } public async Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid tenantId) { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var projectTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.ProjectAllocations .Include(pa => pa.Project) .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null) .GroupBy(pa => pa.ProjectId) .Select(g => new { ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() }).FirstOrDefaultAsync(); }); var roleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.TeamAttendance) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); await Task.WhenAll(projectTask, roleTask); var applicationRoleIds = roleTask.Result; var project = projectTask.Result; List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); var employeeIds = await _context.EmployeeRoleMappings .Where(er => projectAssignedEmployeeIds.Contains(er.EmployeeId) && applicationRoleIds.Contains(er.RoleId)) .Select(er => er.RoleId) .ToListAsync(); string body; switch (markType) { case ATTENDANCE_MARK_TYPE.CHECK_IN: body = $"{Name} Checked In for project {project?.ProjectName ?? ""}"; break; case ATTENDANCE_MARK_TYPE.CHECK_OUT: body = $"{Name} Checked Out for project {project?.ProjectName ?? ""}"; break; case ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE: body = $"{Name} Requested Regularization for project {project?.ProjectName ?? ""}"; break; case ATTENDANCE_MARK_TYPE.REGULARIZE: body = $"Regularization Accepted of {Name} for Project {project?.ProjectName ?? ""}"; break; case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT: body = $"Regularization Rejected of {Name} for Project {project?.ProjectName ?? ""}"; break; default: body = string.Empty; break; } var notificationFirebase = new Notification { Title = "Attendance Marked", Body = body }; // List of device registration tokens to send the message to var registrationTokens = await _context.FCMTokenMappings //.Where(ft => employeeIds.Contains(ft.EmployeeId) && ft.TenantId == tenantId) .Select(ft => ft.FcmToken).ToListAsync(); await SendMessageToMultipleDevicesAsync(registrationTokens, notificationFirebase); } public async Task SendMessageToMultipleDevicesAsync(List registrationTokens, Notification notificationFirebase) { try { var message = new MulticastMessage() { Tokens = registrationTokens, Notification = notificationFirebase }; // Send the multicast message var response = await FirebaseMessaging.DefaultInstance.SendEachForMulticastAsync(message); _logger.LogInfo("{SuccessCount} messages were sent successfully.", response.SuccessCount); if (response.FailureCount > 0) { var failedTokens = new List(); for (int i = 0; i < response.Responses.Count; i++) { if (!response.Responses[i].IsSuccess) { failedTokens.Add(registrationTokens[i]); } } _logger.LogInfo("List of tokens that caused failures: " + string.Join(", ", failedTokens)); } } catch (FirebaseMessagingException ex) { // Log the specific Firebase error. _logger.LogError(ex, "Error sending push notification"); // Check for specific error codes that indicate an invalid or unregistered token. if (ex.MessagingErrorCode == MessagingErrorCode.Unregistered || ex.MessagingErrorCode == MessagingErrorCode.InvalidArgument) { _logger.LogWarning("FCM token is invalid and should be deleted from the database"); // TODO: Implement the logic here to remove the invalid token from your database. // Example: await YourTokenService.DeleteTokenAsync(loginDto.DeviceToken); } } } } }