private async Task AppendDeliveryMessage(DeliveryMetadata deliveryMetadata, string deliveryStreamId,
                                          string messageType, string eventDataJson, CancellationToken cancellationToken)
 {
     var jsonMeta         = JsonConvert.SerializeObject(deliveryMetadata, SerializerSettings);
     var newStreamMessage = new NewStreamMessage(Guid.NewGuid(), messageType, eventDataJson, jsonMeta);
     await _streamStore.AppendToStream(deliveryStreamId, ExpectedVersion.Any, newStreamMessage,
                                       cancellationToken);
 }
        public async Task DeliverNow(CancellationToken cancellationToken = default(CancellationToken))
        {
            using (var cts = CancellationTokenSource.CreateLinkedTokenSource(_disposed.Token, cancellationToken))
            {
                bool messageSent;
                do
                {
                    messageSent = false;
                    var webHooksToProcess = (await _webHooksRepository.Load(cancellationToken))
                                            .Items.Where(w => w.Enabled).ToList();

                    foreach (var webHook in webHooksToProcess)
                    {
                        if (cts.Token.IsCancellationRequested)
                        {
                            return;
                        }
                        var streamIds = webHook.StreamIds;

                        var nextMessageToDeliver = await GetNextMessageToDeliver(streamIds.OutStreamId, cts.Token);

                        if (!nextMessageToDeliver.Found)
                        {
                            continue;
                        }

                        var streamMessageToDeliver = nextMessageToDeliver.StreamMessage;
                        var previousDeliveryInfo   =
                            await GetPreviousDeliveryInfo(streamIds.DeliveriesStreamId, cancellationToken);

                        if (!previousDeliveryInfo.Success &&
                            previousDeliveryInfo.EventId == streamMessageToDeliver.MessageId)
                        {
                            // Should we attempt to deliver?
                            var timeSinceLastAttempt = _settings.GetUtcNow() - previousDeliveryInfo.DeliveryDateUtc;
                            if (timeSinceLastAttempt > _settings.MaxDeliveryAttemptDuration)
                            {
                                var webHooks = await _webHooksRepository.Load(cancellationToken);

                                webHooks.Disable(webHook.Id);
                            }

                            var delay = CalculateDelay(previousDeliveryInfo.DeliveryAttemptCount,
                                                       _settings.MaxRetryDelay);
                            if (_settings.GetUtcNow() - previousDeliveryInfo.DeliveryDateUtc < delay)
                            {
                                continue;
                            }
                        }

                        // Forward the message to the subscriber.
                        var jsonData = await streamMessageToDeliver.GetJsonData(cts.Token);

                        var request =
                            CreateSubscriberRequest(webHook, jsonData, streamMessageToDeliver, webHook.Secret);

                        var response = await _httpClient.SendAsync(request, cts.Token); //TODO try-catch

                        var deliveryMetadata = new DeliveryMetadata
                        {
                            AttemptCount    = previousDeliveryInfo.DeliveryAttemptCount + 1,
                            DeliverySuccess = true,
                            EventId         = streamMessageToDeliver.MessageId,
                            Sequence        = streamMessageToDeliver.StreamVersion
                        };
                        if ((int)response.StatusCode > 299)
                        {
                            // Delivery failed, write error delivery message
                            deliveryMetadata.DeliverySuccess = false;
                            deliveryMetadata.ErrorMessage    = $"Subscriber returned status code {response.StatusCode}";
                            await AppendDeliveryMessage(
                                deliveryMetadata,
                                streamIds.DeliveriesStreamId,
                                streamMessageToDeliver.Type,
                                jsonData,
                                cancellationToken);
                        }
                        else
                        {
                            await AppendDeliveryMessage(
                                deliveryMetadata,
                                streamIds.DeliveriesStreamId,
                                streamMessageToDeliver.Type,
                                jsonData,
                                cancellationToken);

                            await _streamStore.SetStreamMetadata(
                                streamIds.DeliveriesStreamId,
                                ExpectedVersion.Any,
                                maxCount : _settings.DeliveryStreamMaxCount,
                                cancellationToken : cancellationToken);

                            await _streamStore.DeleteMessage(
                                streamIds.OutStreamId,
                                streamMessageToDeliver.MessageId,
                                cts.Token);

                            messageSent = true;
                        }
                    }
                } while (messageSent);
            }
        }