/// <summary>
        /// Update host's state
        /// </summary>
        /// <param name="tryableHost">A statefull host</param>
        /// <param name="response">Algolia's API response</param>
        /// <returns></returns>
        public RetryOutcomeType Decide(StatefulHost tryableHost, AlgoliaHttpResponse response)
        {
            lock (_lock)
            {
                if (!response.IsTimedOut && IsSuccess(response))
                {
                    tryableHost.Up      = true;
                    tryableHost.LastUse = DateTime.UtcNow;
                    return(RetryOutcomeType.Success);
                }
                else if (!response.IsTimedOut && IsRetryable(response))
                {
                    tryableHost.Up      = false;
                    tryableHost.LastUse = DateTime.UtcNow;
                    return(RetryOutcomeType.Retry);
                }
                else if (response.IsTimedOut)
                {
                    tryableHost.Up      = true;
                    tryableHost.LastUse = DateTime.UtcNow;
                    tryableHost.RetryCount++;
                    return(RetryOutcomeType.Retry);
                }

                return(RetryOutcomeType.Failure);
            }
        }
        /// <summary>
        ///  Tells if the response is retryable or not
        /// </summary>
        /// <param name="response">Aloglia's API response</param>
        /// <returns></returns>
        private bool IsRetryable(AlgoliaHttpResponse response)
        {
            var isRetryableHttpCode = (int)Math.Floor((decimal)response.HttpStatusCode / 100) != 2 &&
                                      (int)Math.Floor((decimal)response.HttpStatusCode / 100) != 4;

            return(isRetryableHttpCode || response.IsNetworkError);
        }
        /// <summary>
        /// Call api with retry strategy
        /// </summary>
        /// <typeparam name="TResult">Return type</typeparam>
        /// <typeparam name="TData">Data type</typeparam>
        /// <param name="method">The HttpMethod <see cref="HttpMethod"/></param>
        /// <param name="uri">The endpoint URI</param>
        /// <param name="callType">The method Algolia's call type <see cref="CallType"/> </param>
        /// <param name="data">Your data</param>
        /// <param name="requestOptions">Add extra http header or query parameters to Algolia</param>
        /// <param name="ct">Optional cancellation token</param>
        public async Task <TResult> ExecuteRequestAsync <TResult, TData>(HttpMethod method, string uri, CallType callType,
                                                                         TData data           = default, RequestOptions requestOptions = null,
                                                                         CancellationToken ct = default)
            where TResult : class
            where TData : class
        {
            if (string.IsNullOrWhiteSpace(uri))
            {
                throw new ArgumentNullException(nameof(uri));
            }

            if (method == null)
            {
                throw new ArgumentNullException(nameof(method));
            }

            var request = new Request
            {
                Method      = method,
                Headers     = GenerateHeaders(requestOptions?.Headers),
                Compression = _algoliaConfig.Compression
            };

            foreach (var host in _retryStrategy.GetTryableHost(callType))
            {
                request.Body = CreateRequestContent(data, request.CanCompress);
                request.Uri  = BuildUri(host.Url, uri, requestOptions?.QueryParameters);
                int requestTimeout = (requestOptions?.Timeout ?? GetTimeOut(callType)) * (host.RetryCount + 1);

                AlgoliaHttpResponse response = await _httpClient
                                               .SendRequestAsync(request, requestTimeout, ct)
                                               .ConfigureAwait(false);

                errorMessage = response.Error;

                switch (_retryStrategy.Decide(host, response))
                {
                case RetryOutcomeType.Success:
                    return(_serializer.Deserialize <TResult>(response.Body));

                case RetryOutcomeType.Retry:
                    continue;

                case RetryOutcomeType.Failure:
                    throw new AlgoliaApiException(response.Error, response.HttpStatusCode);
                }
            }

            throw new AlgoliaUnreachableHostException("RetryStrategy failed to connect to Algolia. Reason: " + errorMessage);
        }
 /// <summary>
 ///  Tells if the response is a success or not
 /// </summary>
 /// <param name="response">Aloglia's API response</param>
 /// <returns></returns>
 private bool IsSuccess(AlgoliaHttpResponse response)
 {
     return((int)Math.Floor((decimal)response.HttpStatusCode / 100) == 2);
 }