public Task <Result> NotifyDeadlineApproaching(int bookingId, string email)
        {
            return(_bookingRecordManager.Get(bookingId)
                   .Bind(async booking =>
            {
                var roomDescriptions = booking.Rooms
                                       .Select(r => r.ContractDescription);

                var passengers = booking.Rooms
                                 .SelectMany(r => r.Passengers)
                                 .Select(p => $"{p.FirstName} {p.LastName}");

                var deadlineData = new BookingDeadlineData
                {
                    BookingId = booking.Id,
                    RoomDescriptions = string.Join(", ", roomDescriptions),
                    Passengers = string.Join(", ", passengers),
                    ReferenceCode = booking.ReferenceCode,
                    CheckInDate = DateTimeFormatters.ToDateString(booking.CheckInDate),
                    CheckOutDate = DateTimeFormatters.ToDateString(booking.CheckOutDate),
                    Deadline = DateTimeFormatters.ToDateString(booking.DeadlineDate)
                };

                return await _notificationService.Send(agent: new SlimAgentContext(agentId: booking.AgentId, agencyId: booking.AgencyId),
                                                       messageData: deadlineData,
                                                       notificationType: NotificationTypes.DeadlineApproaching,
                                                       email: email);
            }));
        }
        public async Task <Result> NotifyCreditCardPaymentConfirmed(string referenceCode)
        {
            return(await GetData()
                   .Tap(SendNotificationToAdmin)
                   .Tap(SendNotificationToAgent));


            async Task <Result <CreditCardPaymentConfirmationNotification> > GetData()
            {
                var query = from booking in _context.Bookings
                            join agent in _context.Agents on booking.AgentId equals agent.Id
                            join agentAgencyRelation in _context.AgentAgencyRelations on agent.Id equals agentAgencyRelation.AgentId
                            join agency in _context.Agencies on agentAgencyRelation.AgencyId equals agency.Id
                            where booking.ReferenceCode == referenceCode
                            select new CreditCardPaymentConfirmationNotification
                {
                    Agency           = agency.Name,
                    Agent            = $"{agent.FirstName} {agent.LastName}",
                    ReferenceCode    = booking.ReferenceCode,
                    Accommodation    = booking.AccommodationName,
                    Location         = $"{booking.Location.Country}, {booking.Location.Locality}",
                    LeadingPassenger = booking.GetLeadingPassengerFormattedName(),
                    Amount           = MoneyFormatter.ToCurrencyString(booking.TotalPrice, booking.Currency),
                    DeadlineDate     = DateTimeFormatters.ToDateString(booking.DeadlineDate),
                    CheckInDate      = DateTimeFormatters.ToDateString(booking.CheckInDate),
                    CheckOutDate     = DateTimeFormatters.ToDateString(booking.CheckOutDate),
                    Status           = EnumFormatters.FromDescription(booking.Status),
                    PaymentStatus    = EnumFormatters.FromDescription(booking.PaymentStatus),
                    Email            = agent.Email
                };

                var data = await query.SingleOrDefaultAsync();

                return(data ?? Result.Failure <CreditCardPaymentConfirmationNotification>($"Booking with reference code {referenceCode} not found"));
            }

            Task SendNotificationToAdmin(CreditCardPaymentConfirmationNotification data)
            => _notificationService.Send(messageData: data,
                                         notificationType: NotificationTypes.CreditCardPaymentReceivedAdministrator,
                                         emails: _options.CcNotificationAddresses);


            async Task SendNotificationToAgent(CreditCardPaymentConfirmationNotification data)
            {
                var booking = await _context.Bookings.SingleOrDefaultAsync(b => b.ReferenceCode == data.ReferenceCode);

                await _notificationService.Send(agent : new SlimAgentContext(booking.AgentId, booking.AgencyId),
                                                messageData : data,
                                                notificationType : NotificationTypes.CreditCardPaymentReceived,
                                                email : data.Email);
            }
        }
        private static BookingNotificationData CreateNotificationData(AccommodationBookingInfo bookingInfo, AccommodationBookingDetails details)
        {
            return(new BookingNotificationData
            {
                AgentName = bookingInfo.AgentInformation.AgentName,
                BookingDetails = new BookingNotificationData.Details
                {
                    AccommodationName = details.AccommodationName,
                    CheckInDate = DateTimeFormatters.ToDateString(details.CheckInDate),
                    CheckOutDate = DateTimeFormatters.ToDateString(details.CheckOutDate),
                    DeadlineDate = DateTimeFormatters.ToDateString(details.DeadlineDate),
                    Location = details.Location,
                    NumberOfNights = details.NumberOfNights,
                    NumberOfPassengers = details.NumberOfPassengers,
                    ReferenceCode = details.ReferenceCode,
                    RoomDetails = details.RoomDetails.Select(d =>
                    {
                        var maskedPassengers = d.Passengers.Where(p => p.IsLeader)
                                               .Select(p =>
                        {
                            var firstName = p.FirstName.Length == 1 ? "*" : p.FirstName.Substring(0, 1);
                            return new Pax(p.Title, p.LastName, firstName);
                        })
                                               .ToList();

                        return new BookingNotificationData.BookedRoomDetails
                        {
                            ContractDescription = d.ContractDescription,
                            MealPlan = d.MealPlan,
                            Passengers = maskedPassengers,
                            Price = MoneyFormatter.ToCurrencyString(d.Price.Amount, d.Price.Currency),
                            Type = EnumFormatters.FromDescription(d.Type),
                            Remarks = d.Remarks
                        };
                    }).ToList(),
                    Status = EnumFormatters.FromDescription(details.Status),
                    SupplierReferenceCode = details.AgentReference,
                    ContactInfo = details.ContactInfo,
                },
                AgencyName = bookingInfo.AgentInformation.AgencyName,
                PaymentStatus = EnumFormatters.FromDescription(bookingInfo.PaymentStatus),
                Price = MoneyFormatter.ToCurrencyString(bookingInfo.TotalPrice.Amount, bookingInfo.TotalPrice.Currency),
                CancellationPenalty = MoneyFormatter.ToCurrencyString(bookingInfo.CancellationPenalty.Amount, bookingInfo.CancellationPenalty.Currency),
                Supplier = bookingInfo.Supplier is null
                    ? string.Empty
                    : EnumFormatters.FromDescription(bookingInfo.Supplier.Value),
            });