/// <summary>
        /// Initializes a new instance of the <see cref="ApiHeaders"/> class.
        /// </summary>
        /// <param name="requestHeaders">The <see cref="RequestHeaders"/> containing the serialized <see cref="ApiHeaders"/>.</param>
        /// <param name="ignoreMissingAuth">If a missing <see cref="HeaderNames.Authorization"/> should be ignored.</param>
        /// <exception cref="HeadersException">Thrown if the <paramref name="requestHeaders"/> constitue invalid <see cref="ApiHeaders"/>.</exception>
#pragma warning disable CA1502
        public ApiHeaders(RequestHeaders requestHeaders, bool ignoreMissingAuth = false)
        {
            if (requestHeaders == null)
            {
                throw new ArgumentNullException(nameof(requestHeaders));
            }

            var badHeaders   = HeaderTypes.None;
            var errorBuilder = new StringBuilder();

            void AddError(HeaderTypes headerType, string message)
            {
                if (badHeaders != HeaderTypes.None)
                {
                    errorBuilder.AppendLine();
                }
                badHeaders |= headerType;
                errorBuilder.Append(message);
            }

            var jsonAccept = new Microsoft.Net.Http.Headers.MediaTypeHeaderValue(MediaTypeNames.Application.Json);

            if (!requestHeaders.Accept.Any(x => jsonAccept.IsSubsetOf(x)))
            {
                AddError(HeaderTypes.Accept, $"Client does not accept {MediaTypeNames.Application.Json}!");
            }

            if (!requestHeaders.Headers.TryGetValue(HeaderNames.UserAgent, out var userAgentValues) || userAgentValues.Count == 0)
            {
                AddError(HeaderTypes.UserAgent, $"Missing {HeaderNames.UserAgent} header!");
            }
            else
            {
                RawUserAgent = userAgentValues.First();
                if (String.IsNullOrWhiteSpace(RawUserAgent))
                {
                    AddError(HeaderTypes.UserAgent, $"Malformed {HeaderNames.UserAgent} header!");
                }
            }

            // make sure the api header matches ours
            Version?apiVersion = null;

            if (!requestHeaders.Headers.TryGetValue(ApiVersionHeader, out var apiUserAgentHeaderValues) || !ProductInfoHeaderValue.TryParse(apiUserAgentHeaderValues.FirstOrDefault(), out var apiUserAgent) || apiUserAgent.Product.Name != AssemblyName.Name)
            {
                AddError(HeaderTypes.Api, $"Missing {ApiVersionHeader} header!");
            }
            else if (!Version.TryParse(apiUserAgent.Product.Version, out apiVersion))
            {
                AddError(HeaderTypes.Api, $"Malformed {ApiVersionHeader} header!");
            }

            if (!requestHeaders.Headers.TryGetValue(HeaderNames.Authorization, out StringValues authorization))
            {
                if (!ignoreMissingAuth)
                {
                    AddError(HeaderTypes.Authorization, $"Missing {HeaderNames.Authorization} header!");
                }
            }
            else
            {
                var auth   = authorization.First();
                var splits = new List <string>(auth.Split(' '));
                var scheme = splits.First();
                if (String.IsNullOrWhiteSpace(scheme))
                {
                    AddError(HeaderTypes.Authorization, "Missing authentication scheme!");
                }
                else
                {
                    splits.RemoveAt(0);
                    var parameter = String.Concat(splits);
                    if (String.IsNullOrEmpty(parameter))
                    {
                        AddError(HeaderTypes.Authorization, "Missing authentication parameter!");
                    }
                    else
                    {
                        if (requestHeaders.Headers.TryGetValue(InstanceIdHeader, out var instanceIdValues))
                        {
                            var instanceIdString = instanceIdValues.FirstOrDefault();
                            if (instanceIdString != default && Int64.TryParse(instanceIdString, out var instanceId))
                            {
                                InstanceId = instanceId;
                            }
                        }

                        switch (scheme)
                        {
                        case OAuthAuthenticationScheme:
                            if (requestHeaders.Headers.TryGetValue(OAuthProviderHeader, out StringValues oauthProviderValues))
                            {
                                var oauthProviderString = oauthProviderValues.First();
                                if (Enum.TryParse <OAuthProvider>(oauthProviderString, out var oauthProvider))
                                {
                                    OAuthProvider = oauthProvider;
                                }
                                else
                                {
                                    AddError(HeaderTypes.OAuthProvider, "Invalid OAuth provider!");
                                }
                            }
                            else
                            {
                                AddError(HeaderTypes.OAuthProvider, $"Missing {OAuthProviderHeader} header!");
                            }

                            goto case BearerAuthenticationScheme;

                        case BearerAuthenticationScheme:
                            Token = parameter;
                            break;

                        case BasicAuthenticationScheme:
                            string badBasicAuthHeaderMessage = $"Invalid basic {HeaderNames.Authorization} header!";
                            string joinedString;
                            try
                            {
                                var base64Bytes = Convert.FromBase64String(parameter);
                                joinedString = Encoding.UTF8.GetString(base64Bytes);
                            }
                            catch
                            {
                                AddError(HeaderTypes.Authorization, badBasicAuthHeaderMessage);
                                break;
                            }

                            var basicAuthSplits = joinedString.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                            if (basicAuthSplits.Length < 2)
                            {
                                AddError(HeaderTypes.Authorization, badBasicAuthHeaderMessage);
                                break;
                            }

                            Username = basicAuthSplits.First();
                            Password = String.Concat(basicAuthSplits.Skip(1));
                            break;

                        default:
                            AddError(HeaderTypes.Authorization, "Invalid authentication scheme!");
                            break;
                        }
                    }
                }
            }

            if (badHeaders != HeaderTypes.None)
            {
                throw new HeadersException(badHeaders, errorBuilder.ToString());
            }

            ApiVersion = apiVersion !.Semver();
        }