/// <summary>Process an HTTP GET in FHIR R4</summary>
        /// <param name="context"> [in,out] The context.</param>
        /// <param name="response">[in,out] The response.</param>
        internal static void ProcessGet(ref HttpContext context, ref HttpResponseMessage response)
        {
            // check for an ID
            string requestUrl = context.Request.Path;

            if (requestUrl.EndsWith('/'))
            {
                requestUrl = requestUrl.Substring(0, requestUrl.Length - 1);
            }

            string id = requestUrl.Substring(requestUrl.LastIndexOf('/') + 1);

            if (id.ToLowerInvariant() == "subscription")
            {
                ProcessorUtils.SerializeR4(ref response, SubscriptionManagerR4.GetSubscriptionsBundle());
            }
            else if (SubscriptionManagerR4.TryGetSubscription(id, out Subscription foundSub))
            {
                ProcessorUtils.SerializeR4(ref response, foundSub);
            }
            else
            {
                response.StatusCode = HttpStatusCode.NotFound;
            }
        }
        /// <summary>Process the operation get ws binding token described by response.</summary>
        /// <param name="context">          [in,out] The context.</param>
        /// <param name="response">         [in,out] The response.</param>
        /// <param name="previousComponent">The previous component.</param>
        internal static void ProcessOperationGetWsBindingToken(
            ref HttpContext context,
            ref HttpResponseMessage response,
            string previousComponent)
        {
            List <string> ids = new List <string>();

            if (previousComponent != "Subscription")
            {
                ids.Add(previousComponent);
            }
            else
            {
                try
                {
                    using (TextReader reader = new StreamReader(context.Request.Body))
                    {
                        string requestContent = reader.ReadToEndAsync().Result;

                        Parameters opParams = _fhirParser.Parse <Parameters>(requestContent);

                        foreach (Parameters.ParameterComponent param in opParams.Parameter)
                        {
                            if (param.Name == "ids")
                            {
                                ids.Add((param.Value as Id).ToString());
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    response.StatusCode = HttpStatusCode.BadRequest;
                    response.Content    = new StringContent("Caught exception: " + ex.Message);
                    return;
                }
            }

            foreach (string id in ids)
            {
                if (!SubscriptionManagerR4.Exists(id))
                {
                    response.StatusCode = HttpStatusCode.BadRequest;
                    response.Content    = new StringContent($"Invalid subscription id: {id}");
                    return;
                }
            }

            SubscriptionWsBindingToken token = SubscriptionWsBindingToken.GetTokenR4(ids);

            WebsocketManager.RegisterToken(token);

            Parameters parameters = new Parameters();

            parameters.Add("token", new FhirString(token.Token.ToString()));
            parameters.Add("expiration", new FhirDateTime(new DateTimeOffset(token.ExpiresAt)));
            parameters.Add("subscriptions", new FhirString(string.Join(',', ids)));

            ProcessorUtils.SerializeR4(ref response, parameters);
        }
        /// <summary>Process the operation topic list R4.</summary>
        /// <param name="response">[in,out] The response.</param>
        internal static void ProcessOperationTopicList(
            ref HttpResponseMessage response)
        {
            Parameters topics = new Parameters();

            foreach (string topicUrl in SubscriptionTopicManager.GetTopicUrlList())
            {
                topics.Add("subscription-topic-canonical", new Canonical(topicUrl));
            }

            ProcessorUtils.SerializeR4(ref response, topics);
        }
        /// <summary>Process the operation status R4.</summary>
        /// <param name="context">          [in,out] The context.</param>
        /// <param name="response">         [in,out] The response.</param>
        /// <param name="previousComponent">The previous component.</param>
        internal static void ProcessOperationStatus(
            ref HttpContext context,
            ref HttpResponseMessage response,
            string previousComponent)
        {
            List <string> ids = new List <string>();

            if (previousComponent != "Subscription")
            {
                ids.Add(previousComponent);
            }
            else
            {
                foreach (KeyValuePair <string, StringValues> query in context.Request.Query)
                {
                    if (query.Key == "ids")
                    {
                        ids.AddRange(query.Value);
                    }
                }
            }

            // create a bundle for this message message
            Bundle bundle = new Bundle()
            {
                Type      = Bundle.BundleType.Searchset,
                Timestamp = new DateTimeOffset(DateTime.Now),
                Meta      = new Meta(),
                Entry     = new List <Bundle.EntryComponent>(),
            };

            foreach (string id in ids)
            {
                if (SubscriptionManagerR4.TryGetSubscriptionStatus(id, out Parameters status, 0, true))
                {
                    bundle.Entry.Add(new Bundle.EntryComponent()
                    {
                        FullUrl  = Program.UrlForR5ResourceId(status.TypeName, status.Id),
                        Resource = status,
                        Search   = new Bundle.SearchComponent()
                        {
                            Mode = Bundle.SearchEntryMode.Match,
                        },
                    });
                }
            }

            ProcessorUtils.SerializeR4(ref response, bundle);
        }
        /// <summary>Process an R4 metadata GET.</summary>
        /// <param name="context"> [in,out] The context.</param>
        /// <param name="response">[in,out] The response.</param>
        private static void ProcessGet(
            ref HttpContext context,
            ref HttpResponseMessage response)
        {
            // check for a valid capability statement
            try
            {
                // grab the message body to look at
                string responseContent = response.Content.ReadAsStringAsync().Result;

                CapabilityStatement capabilities = _fhirParser.Parse <CapabilityStatement>(responseContent);

                if (capabilities.Software == null)
                {
                    capabilities.Software = new CapabilityStatement.SoftwareComponent()
                    {
                        Name    = $"Argo-Proxy: {Program.FhirServerUrlR5}",
                        Version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).FileVersion.ToString(),
                    };
                }
                else
                {
                    capabilities.Software.Name    = $"Argo-Proxy: {capabilities.Software.Name}";
                    capabilities.Software.Version =
                        FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).FileVersion.ToString()
                        + ": " + capabilities.Software.Version;
                }

                capabilities.Implementation = new CapabilityStatement.ImplementationComponent()
                {
                    Description = $"Argonaut Subscription Proxy to: {Program.FhirServerUrlR5}",
                    Url         = Program.PublicUrl,
                };

                // only support application/fhir+json
                capabilities.Format = new string[] { "application/fhir+json" };

                // make sure Subscription is present
                bool foundSubscription = false;
                bool foundWebSocketUrl = false;

                for (int restIndex = 0;
                     restIndex < capabilities.Rest.Count;
                     restIndex++)
                {
                    for (int resourceIndex = 0;
                         resourceIndex < capabilities.Rest[restIndex].Resource.Count;
                         resourceIndex++)
                    {
                        switch (capabilities.Rest[restIndex].Resource[resourceIndex].TypeName)
                        {
                        case "Subscription":
                            foundSubscription = true;
                            capabilities.Rest[restIndex].Resource[resourceIndex] = BuildSubscriptionResourceComponent();
                            break;
                        }
                    }

                    if (capabilities.Rest[restIndex].Extension != null)
                    {
                        for (int extIndex = 0;
                             extIndex < capabilities.Rest[restIndex].Extension.Count;
                             extIndex++)
                        {
                            if (capabilities.Rest[restIndex].Extension[extIndex].Url == ExtensionCapabilityWebsocket)
                            {
                                foundWebSocketUrl = true;
                                capabilities.Rest[restIndex].Extension[extIndex].Value = new Hl7.Fhir.Model.FhirUri(Program.WebsocketUrl);
                            }
                        }
                    }
                }

                if (!foundSubscription)
                {
                    capabilities.Rest[0].Resource.Add(BuildSubscriptionResourceComponent());
                }

                if (!foundWebSocketUrl)
                {
                    if (capabilities.Rest[0].Extension == null)
                    {
                        capabilities.Rest[0].Extension = new System.Collections.Generic.List <Hl7.Fhir.Model.Extension>();
                    }

                    capabilities.Rest[0].Extension.Add(new Hl7.Fhir.Model.Extension()
                    {
                        Url   = ExtensionCapabilityWebsocket,
                        Value = new Hl7.Fhir.Model.FhirUri(Program.WebsocketUrl),
                    });
                }

                // serialize
                ProcessorUtils.SerializeR4(ref response, capabilities);
            }
            catch (Exception ex)
            {
                // write to console
                Console.WriteLine($"Failed to parse capability statement, {ex.Message}");
            }
        }