public async Task <TraktResponse <ITraktDevice> > GetDeviceAsync(DeviceRequest request, CancellationToken cancellationToken = default)
        {
            try
            {
                request.Validate();

                ExtendedHttpRequestMessage requestMessage =
                    await _requestMessageBuilder.Reset(request)
                    .WithRequestBody(request.RequestBody)
                    .DisableAPIVersionHeader()
                    .DisableAPIClientIdHeader()
                    .Build().ConfigureAwait(false);

                HttpResponseMessage responseMessage = await _client.HttpClientProvider.GetHttpClient().SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);

                if (!responseMessage.IsSuccessStatusCode)
                {
                    await ResponseErrorHandler.HandleErrorsAsync(requestMessage, responseMessage, isDeviceRequest : true, cancellationToken : cancellationToken).ConfigureAwait(false);
                }

                DebugAsserter.AssertResponseMessageIsNotNull(responseMessage);
                DebugAsserter.AssertHttpResponseCodeIsExpected(responseMessage.StatusCode, HttpStatusCode.OK, DebugAsserter.SINGLE_ITEM_RESPONSE_PRECONDITION_INVALID_STATUS_CODE);

                Stream responseContentStream = await ResponseMessageHelper.GetResponseContentStreamAsync(responseMessage).ConfigureAwait(false);

                IObjectJsonReader <ITraktDevice> objectJsonReader = JsonFactoryContainer.CreateObjectReader <ITraktDevice>();
                ITraktDevice device = await objectJsonReader.ReadObjectAsync(responseContentStream, cancellationToken).ConfigureAwait(false);

                var response = new TraktResponse <ITraktDevice>()
                {
                    Value     = device,
                    HasValue  = device != null,
                    IsSuccess = device != null
                };

                if (responseMessage.Headers != null)
                {
                    ResponseHeaderParser.ParseResponseHeaderValues(response, responseMessage.Headers);
                }

                _client.Authentication.Device = device;
                return(response);
            }
            catch (Exception ex)
            {
                if (_client.Configuration.ThrowResponseExceptions)
                {
                    throw;
                }

                return(new TraktResponse <ITraktDevice> {
                    IsSuccess = false, Exception = ex
                });
            }
        }
        private async Task <TraktResponse <TResponseContentType> > QuerySingleItemAsync <TResponseContentType>(ExtendedHttpRequestMessage requestMessage, bool isCheckinRequest = false, CancellationToken cancellationToken = default)
        {
            HttpResponseMessage responseMessage = null;

            try
            {
                responseMessage = await ExecuteRequestAsync(requestMessage, isCheckinRequest, cancellationToken).ConfigureAwait(false);

                DebugAsserter.AssertResponseMessageIsNotNull(responseMessage);
                DebugAsserter.AssertHttpResponseCodeIsNotExpected(responseMessage.StatusCode, HttpStatusCode.NoContent, DebugAsserter.SINGLE_ITEM_RESPONSE_PRECONDITION_INVALID_STATUS_CODE);
                Stream responseContentStream = await ResponseMessageHelper.GetResponseContentStreamAsync(responseMessage).ConfigureAwait(false);

                DebugAsserter.AssertResponseContentStreamIsNotNull(responseContentStream);
                IObjectJsonReader <TResponseContentType> objectJsonReader = JsonFactoryContainer.CreateObjectReader <TResponseContentType>();
                DebugAsserter.AssertObjectJsonReaderIsNotNull(objectJsonReader);
                TResponseContentType contentObject = await objectJsonReader.ReadObjectAsync(responseContentStream, cancellationToken).ConfigureAwait(false);

                bool hasValue = !EqualityComparer <TResponseContentType> .Default.Equals(contentObject, default);

                var response = new TraktResponse <TResponseContentType>
                {
                    IsSuccess = true,
                    HasValue  = hasValue,
                    Value     = contentObject
                };

                if (responseMessage.Headers != null)
                {
                    ResponseHeaderParser.ParseResponseHeaderValues(response, responseMessage.Headers);
                }

                return(response);
            }
            catch (Exception ex)
            {
                if (_client.Configuration.ThrowResponseExceptions)
                {
                    throw;
                }

                return(new TraktResponse <TResponseContentType> {
                    IsSuccess = false, Exception = ex
                });
            }
            finally
            {
                responseMessage?.Dispose();
            }
        }
        private async Task <TraktPagedResponse <TResponseContentType> > QueryPagedListAsync <TResponseContentType>(ExtendedHttpRequestMessage requestMessage, CancellationToken cancellationToken = default)
        {
            HttpResponseMessage responseMessage = null;

            try
            {
                responseMessage = await ExecuteRequestAsync(requestMessage, false, cancellationToken).ConfigureAwait(false);

                DebugAsserter.AssertResponseMessageIsNotNull(responseMessage);
                DebugAsserter.AssertHttpResponseCodeIsNotExpected(responseMessage.StatusCode, HttpStatusCode.NoContent, DebugAsserter.PAGED_LIST_RESPONSE_PRECONDITION_INVALID_STATUS_CODE);
                Stream responseContentStream = await ResponseMessageHelper.GetResponseContentStreamAsync(responseMessage).ConfigureAwait(false);

                DebugAsserter.AssertResponseContentStreamIsNotNull(responseContentStream);
                IArrayJsonReader <TResponseContentType> arrayJsonReader = new ArrayJsonReader <TResponseContentType>();
                DebugAsserter.AssertArrayJsonReaderIsNotNull(arrayJsonReader);
                IEnumerable <TResponseContentType> contentObject = await arrayJsonReader.ReadArrayAsync(responseContentStream, cancellationToken).ConfigureAwait(false);

                var response = new TraktPagedResponse <TResponseContentType>
                {
                    IsSuccess = true,
                    HasValue  = contentObject != null,
                    Value     = contentObject
                };

                if (responseMessage.Headers != null)
                {
                    ResponseHeaderParser.ParsePagedResponseHeaderValues(response, responseMessage.Headers);
                }

                return(response);
            }
            catch (Exception ex)
            {
                if (_client.Configuration.ThrowResponseExceptions)
                {
                    throw;
                }

                return(new TraktPagedResponse <TResponseContentType> {
                    IsSuccess = false, Exception = ex
                });
            }
            finally
            {
                responseMessage?.Dispose();
            }
        }
        internal static async Task HandleErrorsAsync(ExtendedHttpRequestMessage requestMessage, HttpResponseMessage responseMessage,
                                                     bool isCheckinRequest               = false, bool isDeviceRequest       = false, bool isInAuthorizationPolling = false,
                                                     bool isAuthorizationRequest         = false, bool isAuthorizationRevoke = false,
                                                     CancellationToken cancellationToken = default)
        {
            if (requestMessage == null)
            {
                throw new ArgumentNullException(nameof(requestMessage));
            }

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

            string responseContent = string.Empty;

            if (responseMessage.Content != null)
            {
                responseContent = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
            }

            var errorParameters = new ResponseErrorParameters
            {
                IsCheckinRequest   = isCheckinRequest,
                RequestBody        = requestMessage.RequestBodyJson,
                ResponseBody       = responseContent ?? string.Empty,
                ServerReasonPhrase = responseMessage.ReasonPhrase,
                StatusCode         = responseMessage.StatusCode,
                Url = requestMessage.Url,
                RequestObjectType        = requestMessage.RequestObjectType ?? RequestObjectType.Unspecified,
                ObjectId                 = requestMessage.ObjectId,
                SeasonNumber             = requestMessage.SeasonNumber ?? 0,
                EpisodeNumber            = requestMessage.EpisodeNumber ?? 0,
                IsDeviceRequest          = isDeviceRequest,
                IsInAuthorizationPolling = isInAuthorizationPolling,
                IsAuthorizationRequest   = isAuthorizationRequest,
                IsAuthorizationRevoke    = isAuthorizationRevoke
            };

            ResponseHeaderParser.ParsePagedResponseHeaderValues(errorParameters.Headers, responseMessage.Headers);
            await HandleErrorsAsync(errorParameters, cancellationToken).ConfigureAwait(false);
        }
        private async Task <TraktResponse <ITraktAuthorization> > ExecuteAuthorizationRequestAsync <TRequestBodyType>(IPostRequest <ITraktAuthorization, TRequestBodyType> request, bool isRefreshRequest, CancellationToken cancellationToken = default) where TRequestBodyType : IRequestBody
        {
            try
            {
                request.Validate();

                ExtendedHttpRequestMessage requestMessage =
                    await _requestMessageBuilder.Reset(request)
                    .WithRequestBody(request.RequestBody)
                    .DisableAPIVersionHeader()
                    .DisableAPIClientIdHeader()
                    .Build().ConfigureAwait(false);

                HttpResponseMessage responseMessage = await _client.HttpClientProvider.GetHttpClient().SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);

                HttpStatusCode responseCode = responseMessage.StatusCode;
                Stream         responseContentStream;

                if (responseCode == HttpStatusCode.OK)
                {
                    responseContentStream = await ResponseMessageHelper.GetResponseContentStreamAsync(responseMessage).ConfigureAwait(false);

                    IObjectJsonReader <ITraktAuthorization> objectJsonReader = JsonFactoryContainer.CreateObjectReader <ITraktAuthorization>();
                    ITraktAuthorization traktAuthorization = await objectJsonReader.ReadObjectAsync(responseContentStream, cancellationToken).ConfigureAwait(false);

                    var response = new TraktResponse <ITraktAuthorization>()
                    {
                        Value     = traktAuthorization,
                        HasValue  = traktAuthorization != null,
                        IsSuccess = traktAuthorization != null
                    };

                    if (responseMessage.Headers != null)
                    {
                        ResponseHeaderParser.ParseResponseHeaderValues(response, responseMessage.Headers);
                    }

                    _client.Authentication.Authorization = traktAuthorization;
                    return(response);
                }
                else if (responseCode == HttpStatusCode.Unauthorized) // Invalid code
                {
                    responseContentStream = await ResponseMessageHelper.GetResponseContentStreamAsync(responseMessage).ConfigureAwait(false);

                    IObjectJsonReader <ITraktError> objectJsonReader = JsonFactoryContainer.CreateObjectReader <ITraktError>();
                    ITraktError traktError = await objectJsonReader.ReadObjectAsync(responseContentStream, cancellationToken).ConfigureAwait(false);

                    var errorMessage = traktError != null ? ($"error on {(isRefreshRequest ? "refreshing" : "retrieving")} oauth access token\nerror: {traktError.Error}\ndescription: {traktError.Description}")
                                                          : "unknown error";

                    throw new TraktAuthenticationOAuthException(errorMessage)
                          {
                              StatusCode         = responseCode,
                              RequestUrl         = requestMessage.Url,
                              RequestBody        = requestMessage.RequestBodyJson,
                              ServerReasonPhrase = responseMessage.ReasonPhrase
                          };
                }

                await ResponseErrorHandler.HandleErrorsAsync(requestMessage, responseMessage, isAuthorizationRequest : true, cancellationToken : cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                if (_client.Configuration.ThrowResponseExceptions)
                {
                    throw;
                }

                return(new TraktResponse <ITraktAuthorization> {
                    IsSuccess = false, Exception = ex
                });
            }

            return(new TraktResponse <ITraktAuthorization>());
        }
        public async Task <TraktResponse <ITraktAuthorization> > PollForAuthorizationAsync(AuthorizationPollRequest request, CancellationToken cancellationToken = default)
        {
            try
            {
                request.Validate();

                ExtendedHttpRequestMessage requestMessage =
                    await _requestMessageBuilder.Reset(request)
                    .WithRequestBody(request.RequestBody)
                    .DisableAPIVersionHeader()
                    .DisableAPIClientIdHeader()
                    .Build().ConfigureAwait(false);

                HttpResponseMessage responseMessage;
                Stream         responseContentStream;
                HttpStatusCode responseCode;
                string         reasonPhrase        = string.Empty;
                uint           totalExpiredSeconds = 0;
                ITraktDevice   device = request.RequestBody.Device;
                IObjectJsonReader <ITraktAuthorization> objectJsonReader = JsonFactoryContainer.CreateObjectReader <ITraktAuthorization>();

                while (totalExpiredSeconds < device.ExpiresInSeconds)
                {
                    responseMessage = await _client.HttpClientProvider.GetHttpClient().SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);

                    responseCode = responseMessage.StatusCode;
                    reasonPhrase = responseMessage.ReasonPhrase;

                    if (responseCode == HttpStatusCode.OK) // Success
                    {
                        responseContentStream = await ResponseMessageHelper.GetResponseContentStreamAsync(responseMessage).ConfigureAwait(false);

                        ITraktAuthorization traktAuthorization = await objectJsonReader.ReadObjectAsync(responseContentStream, cancellationToken).ConfigureAwait(false);

                        var response = new TraktResponse <ITraktAuthorization>()
                        {
                            Value     = traktAuthorization,
                            HasValue  = traktAuthorization != null,
                            IsSuccess = traktAuthorization != null
                        };

                        if (responseMessage.Headers != null)
                        {
                            ResponseHeaderParser.ParseResponseHeaderValues(response, responseMessage.Headers);
                        }

                        _client.Authentication.Authorization = traktAuthorization;
                        return(response);
                    }
                    else if (responseCode == HttpStatusCode.BadRequest) // Pending
                    {
                        await Task.Delay((int)device.IntervalInMilliseconds).ConfigureAwait(false);

                        totalExpiredSeconds += device.IntervalInSeconds;
                        requestMessage       = await _requestMessageBuilder.Reset(request).WithRequestBody(request.RequestBody).Build().ConfigureAwait(false);

                        continue;
                    }

                    await ResponseErrorHandler.HandleErrorsAsync(requestMessage, responseMessage, isInAuthorizationPolling : true, cancellationToken : cancellationToken).ConfigureAwait(false);

                    break;
                }

                throw new TraktAuthenticationDeviceException("unknown exception")
                      {
                          RequestUrl         = requestMessage.Url,
                          RequestBody        = requestMessage.RequestBodyJson,
                          ServerReasonPhrase = reasonPhrase
                      };
            }
            catch (Exception ex)
            {
                if (_client.Configuration.ThrowResponseExceptions)
                {
                    throw;
                }

                return(new TraktResponse <ITraktAuthorization> {
                    IsSuccess = false, Exception = ex
                });
            }
        }