public async Task <Result> Cancel(Booking booking, ApiCaller apiCaller, BookingChangeEvents eventType)
        {
            if (booking.Status == BookingStatuses.Cancelled)
            {
                _logger.LogBookingAlreadyCancelled(booking.ReferenceCode);
                return(Result.Success());
            }

            return(await CheckBookingCanBeCancelled()
                   .Bind(SendCancellationRequest)
                   .Bind(ProcessCancellation)
                   .Finally(WriteLog));


            Result CheckBookingCanBeCancelled()
            {
                if (booking.Status != BookingStatuses.Confirmed)
                {
                    return(Result.Failure("Only confirmed bookings can be cancelled"));
                }

                if (booking.CheckOutDate <= _dateTimeProvider.UtcToday())
                {
                    return(Result.Failure("Cannot cancel booking after check out date"));
                }

                return(Result.Success());
            }

            async Task <Result <Booking> > SendCancellationRequest()
            {
                var(_, isCancelFailure, _, cancelError) = await _supplierConnectorManager.Get((Suppliers)booking.Supplier).CancelBooking(booking.ReferenceCode);

                return(isCancelFailure
                    ? Result.Failure <Booking>(cancelError.Detail)
                    : Result.Success(booking));
            }

            async Task <Result> ProcessCancellation(Booking b)
            {
                var changeReason = new BookingChangeReason
                {
                    Event  = eventType,
                    Source = BookingChangeSources.System
                };

                await _bookingRecordsUpdater.ChangeStatus(b, BookingStatuses.PendingCancellation, _dateTimeProvider.UtcNow(), apiCaller, changeReason);

                return(b.UpdateMode == BookingUpdateModes.Synchronous
                    ? await RefreshStatus(b, apiCaller, eventType)
                    : Result.Success());
            }

            Result WriteLog(Result result)
            => LoggerUtils.WriteLogByResult(result,
                                            () => _logger.LogBookingCancelSuccess(booking.ReferenceCode),
                                            () => _logger.LogBookingCancelFailure(booking.ReferenceCode, result.Error));
        }
        private async Task ProcessBookingNotFound(Data.Bookings.Booking booking, Booking bookingResponse, BookingChangeEvents eventType)
        {
            // If booking was confirmed or
            if (_dateTimeProvider.UtcNow() < booking.Created + BookingCheckTimeout && booking.Status != BookingStatuses.Confirmed)
            {
                _logger.LogBookingResponseProcessSuccess(bookingResponse.ReferenceCode, $"Has not been processed due to '{BookingStatusCodes.NotFound}' status.");
            }
            else
            {
                await _recordsUpdater.ChangeStatus(booking, BookingStatuses.ManualCorrectionNeeded, _dateTimeProvider.UtcNow(), ApiCaller.InternalServiceAccount, new Data.Bookings.BookingChangeReason
                {
                    Source = BookingChangeSources.System,
                    Event  = eventType
                });

                _logger.LogBookingResponseProcessSuccess(bookingResponse.ReferenceCode, "Set as needed manual processing.");
            }
        }
        public async Task ProcessResponse(Booking bookingResponse, ApiCaller apiCaller, BookingChangeEvents eventType)
        {
            var(_, isFailure, booking, error) = await _bookingRecordManager.Get(bookingResponse.ReferenceCode);

            if (isFailure)
            {
                _logger.LogBookingResponseProcessFailure(error);
                return;
            }

            _logger.LogBookingResponseProcessStarted(bookingResponse.ReferenceCode, booking.Status);

            await _bookingAuditLogService.Add(bookingResponse, booking);

            if (bookingResponse.Status == BookingStatusCodes.NotFound)
            {
                await ProcessBookingNotFound(booking, bookingResponse, eventType);

                return;
            }

            await _recordsUpdater.UpdateWithSupplierData(booking, bookingResponse.SupplierReferenceCode, bookingResponse.BookingUpdateMode,
                                                         bookingResponse.Rooms);

            if (bookingResponse.Status.ToInternalStatus() == booking.Status)
            {
                _logger.LogBookingResponseProcessSuccess(bookingResponse.ReferenceCode, "No status changes applied");
                return;
            }

            var changeReason = new BookingChangeReason
            {
                Event  = eventType,
                Source = BookingChangeSources.Supplier
            };

            var(_, isUpdateFailure, updateError) = await _recordsUpdater.ChangeStatus(booking,
                                                                                      bookingResponse.Status.ToInternalStatus(),
                                                                                      _dateTimeProvider.UtcNow(),
                                                                                      apiCaller,
                                                                                      changeReason);

            if (isUpdateFailure)
            {
                _logger.LogBookingResponseProcessFailure(updateError);
                return;
            }

            _logger.LogBookingResponseProcessSuccess(bookingResponse.ReferenceCode, $"New status: {bookingResponse.Status}");
        }
        public async Task <Result> RefreshStatus(Booking booking, ApiCaller apiCaller, BookingChangeEvents eventType)
        {
            var oldStatus     = booking.Status;
            var referenceCode = booking.ReferenceCode;

            var(_, isGetDetailsFailure, newDetails, getDetailsError) = await _supplierConnectorManager
                                                                       .Get((Suppliers)booking.Supplier)
                                                                       .GetBookingDetails(referenceCode, booking.LanguageCode);

            if (isGetDetailsFailure)
            {
                _logger.LogBookingRefreshStatusFailure(referenceCode, getDetailsError.Detail);
                return(Result.Failure(getDetailsError.Detail));
            }

            await _responseProcessor.ProcessResponse(newDetails, apiCaller, eventType);

            _logger.LogBookingRefreshStatusSuccess(referenceCode, oldStatus, newDetails.Status);

            return(Result.Success());
        }