using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Domain; using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Errors; using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Models; using PostFundManagement.Infrastructure.Database; namespace PostFundManagement.Application.Services.Communications; public sealed class CommunicationsService(IDbContextFactory contextFactory, ITenantContextService tenantContext) : ICommunicationsService { public async Task> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var invite = new Domain.Entities.Invite { OrganisationId = request.OrganisationId, InvitedOrganisationId = request.InvitedOrganisationId, AwardId = request.AwardId, ContractId = request.ContractId, Recipient = request.Recipient, CreatedBy = userId, CreatedAt = DateTime.UtcNow }; context.Invites.Add(invite); context.Notifications.Add(new Domain.Entities.Notification { OrganisationId = request.OrganisationId, Recipient = request.Recipient, Platform = request.Platform, Status = NotificationStatus.Pending, Subject = "Invitation to Onboard", Message = request.Message, CreatedAt = DateTime.UtcNow, TenantId = tenantContext.TenantId }); return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New) .ToResult(invite.Map()); })); public async Task> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var invites = await context.Invites.AsNoTracking() .OrderByDescending(i => i.CreatedAt) .Skip((pagination.Page - 1) * pagination.PageSize) .Take(pagination.PageSize) .ToListAsync(cancellationToken); return Result.Ok(invites.Select(i => i.Map()).ToArray()); })); public async Task> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken) => await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var invite = await context.Invites.AsNoTracking().FirstOrDefaultAsync(i => i.Recipient == applicantUserId, cancellationToken); return Result.FailIf(invite is null, NotFoundError.New) .ToResult(invite!.Map()); })); public async Task> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var invites = await context.Invites.AsNoTracking() .Where(i => i.Recipient == applicantUserId) .OrderByDescending(o => o.CreatedAt) .ToListAsync(cancellationToken); return Result.Ok(invites.Select(i => i.Map()).ToArray()); })); public Task CreateNotificationAsync(CreateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public Task UpdateNotificationAsync(UpdateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public Task> GetNotificationsAsync(string organisationIdHash, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public Task> GetNotificationAsync(string notificationIdHash, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } }