/// <summary>
        /// Returns a string corresponding to the compiled HTML, based on an embedded version of `datasetsite.mustache`, the provided `settings`, and `supportedFeedTypes`.
        /// </summary>
        /// <param name="settings">Configuration settings for the dataset site</param>
        /// <param name="supportedFeedTypes">The supplied list auto-generates the metadata associated which each feed using best-practice values.</param>
        /// <returns>String containing human readable list</returns>
        public static string RenderSimpleDatasetSite(DatasetSiteGeneratorSettings settings, List <OpportunityType> supportedFeedTypes)
        {
            // Check input is not null
            if (settings == null)
            {
                throw new ArgumentNullException(nameof(settings));
            }
            if (supportedFeedTypes == null)
            {
                throw new ArgumentNullException(nameof(supportedFeedTypes));
            }

            var supportedOpportunityTypes = supportedFeedTypes.Select(x => OpportunityTypes.Configurations[x]);

            var dataDownloads = supportedOpportunityTypes
                                .Select(x => new DataDownload
            {
                Identifier     = x.Identifier,
                Name           = x.Name,
                AdditionalType = x.SameAs,
                EncodingFormat = OpenActiveMediaTypes.RealtimePagedDataExchange.Version1,
                ContentUrl     = new Uri(settings.OpenDataFeedBaseUrl + x.DefaultFeedPath)
            })
                                .ToList();

            var dataFeedDescriptions = supportedOpportunityTypes.Select(x => x.ThemeDisplayName).Distinct().ToList();

            return(RenderSimpleDatasetSiteFromDataDownloads(settings, dataDownloads, dataFeedDescriptions));
        }
        /// <summary>
        /// Returns a string corresponding to the compiled HTML, based on an embedded version of `datasetsite.mustache`, the provided `settings`, `dataDownloads` and `dataFeedDescriptions`.
        /// </summary>
        /// <param name="settings">Configuration settings for the dataset site</param>
        /// <param name="dataDownloads">A list of DataDownload objects which each describe an available open data feed</param>
        /// <param name="dataFeedDescriptions">A list of strings that each describe the dataset</param>
        /// <returns>Returns a string corresponding to the compiled HTML</returns>
        public static string RenderSimpleDatasetSiteFromDataDownloads(DatasetSiteGeneratorSettings settings, List <DataDownload> dataDownloads, List <string> dataFeedDescriptions)
        {
            // Check input is not null
            if (settings == null)
            {
                throw new ArgumentNullException(nameof(settings));
            }
            if (dataDownloads == null)
            {
                throw new ArgumentNullException(nameof(dataDownloads));
            }
            if (dataFeedDescriptions == null)
            {
                throw new ArgumentNullException(nameof(dataFeedDescriptions));
            }

            if (settings.OpenDataFeedBaseUrl == null)
            {
                throw new ArgumentNullException($"{nameof(settings.OpenDataFeedBaseUrl)} must not be null", nameof(settings));
            }
            if (settings.OpenDataFeedBaseUrl.ToString().EndsWith("/", System.StringComparison.InvariantCulture))
            {
                throw new ArgumentException($"{nameof(settings.OpenDataFeedBaseUrl)} must not contain a trailing /", nameof(settings));
            }
            if (settings.OpenBookingAPIBaseUrl?.ToString().EndsWith("/", System.StringComparison.InvariantCulture) == true)
            {
                throw new ArgumentException($"{nameof(settings.OpenBookingAPIBaseUrl)} must not contain a trailing /", nameof(settings));
            }

            // Pre-process list of feed descriptions
            var dataFeedHumanisedList = dataFeedDescriptions.ToHumanisedList();
            var keywords = new List <string> {
                "Activities",
                "Sports",
                "Physical Activity",
                "OpenActive"
            };

            keywords.InsertRange(0, dataFeedDescriptions);

            // Strongly typed JSON generation based on OpenActive.NET
            var dataset = new Dataset
            {
                Id            = settings.DatasetSiteUrl,
                Url           = settings.DatasetSiteUrl,
                Name          = settings.OrganisationName + " " + dataFeedHumanisedList,
                Description   = $"Near real-time availability and rich descriptions relating to the {dataFeedHumanisedList.ToLowerInvariant()} available from {settings.OrganisationName}",
                Keywords      = keywords,
                License       = new Uri("https://creativecommons.org/licenses/by/4.0/"),
                DiscussionUrl = settings.DatasetDiscussionUrl,
                Documentation = settings.DatasetDocumentationUrl ?? new Uri("https://developer.openactive.io/go/open-opportunity-data"),
                InLanguage    = settings.DatasetLanguages,
                SchemaVersion = new Uri("https://openactive.io/modelling-opportunity-data/2.0/"),
                Publisher     = new OpenActive.NET.Organization
                {
                    Name        = settings.OrganisationName,
                    LegalName   = settings.OrganisationLegalEntity,
                    Description = settings.OrganisationPlainTextDescription,
                    Email       = settings.OrganisationEmail,
                    Url         = settings.OrganisationUrl,
                    Logo        = new OpenActive.NET.ImageObject
                    {
                        Url = settings.OrganisationLogoUrl
                    }
                },
                Distribution    = dataDownloads,
                DatePublished   = settings.DateFirstPublished,
                DateModified    = DateTimeOffset.UtcNow,
                BackgroundImage = new ImageObject {
                    Url = settings.BackgroundImageUrl
                },
                BookingService = settings.PlatformName == null ? null : new BookingService
                {
                    Name            = settings.PlatformName,
                    Url             = settings.PlatformUrl,
                    SoftwareVersion = settings.PlatformVersion
                },
                AccessService = settings.OpenBookingAPIBaseUrl == null ? null : new WebAPI
                {
                    Name                    = "Open Booking API",
                    Description             = $"API that allows for seamless booking experiences to be created for {dataFeedHumanisedList.ToLowerInvariant()} available from {settings.OrganisationName}",
                    Documentation           = settings.OpenBookingAPIDocumentationUrl ?? new Uri("https://developer.openactive.io/go/open-booking-api"),
                    TermsOfService          = settings.OpenBookingAPITermsOfServiceUrl,
                    EndpointURL             = settings.OpenBookingAPIBaseUrl,
                    AuthenticationAuthority = settings.OpenBookingAPIAuthenticationAuthority,
                    ConformsTo              = new List <Uri> {
                        new Uri("https://openactive.io/open-booking-api/EditorsDraft/")
                    },
                    EndpointDescription = new Uri("https://www.openactive.io/open-booking-api/EditorsDraft/swagger.json"),
                    LandingPage         = settings.OpenBookingAPIRegistrationUrl
                }
            };

            return(RenderDatasetSite(dataset));
        }