/// <summary>
        /// Creates a new tenant representing a Marain service that tenants can enroll to use.
        /// </summary>
        /// <param name="tenantStore">The tenant store.</param>
        /// <param name="manifest">
        /// The manifest for the service. The service name in the manifest must be unique across all service tenants.
        /// </param>
        /// <param name="logger">Optional logger.</param>
        /// <returns>The new tenant.</returns>
        public static async Task <ITenant> CreateServiceTenantAsync(this ITenantStore tenantStore, ServiceManifest manifest, ILogger?logger = null)
        {
            if (manifest == null)
            {
                throw new ArgumentNullException(nameof(manifest));
            }

            logger?.LogDebug("Validating service manifest for service creation.");

            await manifest.ValidateAndThrowAsync(tenantStore).ConfigureAwait(false);

            ITenant parent = await tenantStore.GetServiceTenantParentAsync().ConfigureAwait(false);

            logger?.LogDebug(
                "Creating new service tenant '{serviceName}' with well-known GUID '{wellKnownGuid}'",
                manifest.ServiceName,
                manifest.WellKnownTenantGuid);

            ITenant newTenant = await tenantStore.CreateWellKnownChildTenantAsync(
                parent.Id,
                manifest.WellKnownTenantGuid,
                manifest.ServiceName).ConfigureAwait(false);

            logger?.LogDebug(
                "New service tenant '{serviceName}' created with Id '{tenantId}'; updating tenant type and manifest.",
                newTenant.Name,
                newTenant.Id);

            IEnumerable <KeyValuePair <string, object> > properties = PropertyBagValues.Build(p => p
                                                                                              .AddServiceManifest(manifest)
                                                                                              .AddMarainTenantType(MarainTenantType.Service));

            await tenantStore.UpdateTenantAsync(
                newTenant.Id,
                propertiesToSetOrAdd : properties).ConfigureAwait(false);

            logger?.LogInformation(
                "Created new service tenant '{serviceName}' with Id '{tenantId}'.",
                newTenant.Name,
                newTenant.Id);
            return(newTenant);
        }
        /// <summary>
        /// Creates a new tenant representing a client, using a well known Guid.
        /// </summary>
        /// <param name="tenantStore">The tenant store.</param>
        /// <param name="wellKnownGuid">The well known Guid to use when creating the tenant.</param>
        /// <param name="clientName">The name of the new tenant.</param>
        /// <param name="parentId">Optional ID of parent Client tenant.</param>
        /// <param name="logger">Optional logger.</param>
        /// <returns>The new tenant.</returns>
        public static async Task <ITenant> CreateClientTenantWithWellKnownGuidAsync(this ITenantStore tenantStore, Guid wellKnownGuid, string clientName, string?parentId = null, ILogger?logger = null)
        {
            if (string.IsNullOrWhiteSpace(clientName))
            {
                throw new ArgumentException(nameof(clientName));
            }

            ITenant parent;

            if (!string.IsNullOrEmpty(parentId))
            {
                parent = await tenantStore.GetTenantOfTypeAsync(parentId, MarainTenantType.Client).ConfigureAwait(false);
            }
            else
            {
                parent = await tenantStore.GetClientTenantParentAsync().ConfigureAwait(false);
            }

            logger?.LogDebug("Creating new client tenant '{clientName}' with GUID '{wellKnownGuid}'", clientName, wellKnownGuid);
            ITenant newTenant = await tenantStore.CreateWellKnownChildTenantAsync(
                parent.Id,
                wellKnownGuid,
                clientName).ConfigureAwait(false);

            logger?.LogDebug(
                "New client tenant '{clientName}' created with Id '{tenantId}'; updating tenant type.",
                newTenant.Name,
                newTenant.Id);

            await tenantStore.UpdateTenantAsync(
                newTenant.Id,
                propertiesToSetOrAdd : PropertyBagValues.Build(p => p.AddMarainTenantType(MarainTenantType.Client))).ConfigureAwait(false);

            logger?.LogInformation(
                "Created new client tenant '{clientName}' with Id '{tenantId}'.",
                newTenant.Name,
                newTenant.Id);
            return(newTenant);
        }
        /// <summary>
        /// Initialises a tenancy provider to prepare it for use with Marain.
        /// </summary>
        /// <param name="tenantStore">The tenant store.</param>
        /// <param name="force">
        /// If <c>false</c> and the tenant provider contains top-level tenants (other than the expected Client and Service
        /// tenant parents) then an <see cref="InvalidOperationException"/> will be thrown. If <c>true</c>, no exception will
        /// be thrown and the parent tenants will be created regardless.
        /// </param>
        /// <param name="logger">Optional logger.</param>
        /// <remarks>
        /// Invoking this method ensures that the two top-level parent tenants are in place to act as containers for the
        /// Client and Service tenants.
        /// </remarks>
        /// <returns>A <see cref="Task"/> representing the initialisation operation.</returns>
        public static async Task InitialiseTenancyProviderAsync(this ITenantStore tenantStore, bool force = false, ILogger?logger = null)
        {
            ITenant?existingClientTenantParent = null;

            try
            {
                existingClientTenantParent = await tenantStore.GetClientTenantParentAsync().ConfigureAwait(false);
            }
            catch (InvalidOperationException)
            {
                // No-op - this is expected if the provider isn't yet initialised.
            }

            ITenant?existingServiceTenantParent = null;

            try
            {
                existingServiceTenantParent = await tenantStore.GetServiceTenantParentAsync().ConfigureAwait(false);
            }
            catch (InvalidOperationException)
            {
                // No-op - this is expected if the provider isn't yet initialised.
            }

            if (existingClientTenantParent != null && existingServiceTenantParent != null)
            {
                // All done - both parent tenants exist.
                return;
            }

            // At least one of the parent tenants don't exist. If there are other tenants, we're being asked to initialise
            // into a non-empty tenant provider, which we shouldn't do by default. To check this, we only need to retrieve
            // 2 tenants.
            TenantCollectionResult existingTopLevelTenantIds = await tenantStore.GetChildrenAsync(
                tenantStore.Root.Id,
                2,
                null).ConfigureAwait(false);

            if (existingTopLevelTenantIds.Tenants.Count > 1 && !force)
            {
                throw new InvalidOperationException("Cannot initialise the tenancy provider for use with Marain because it already contains non-Marain tenants at the root level. If you wish to initialise anyway, re-invoke the method with the 'force' parameter set to true.");
            }

            // Create the tenants
            if (existingClientTenantParent == null)
            {
                await tenantStore.CreateWellKnownChildTenantAsync(
                    tenantStore.Root.Id,
                    WellKnownTenantIds.ClientTenantParentGuid,
                    TenantNames.ClientTenantParent).ConfigureAwait(false);

                logger?.LogInformation("Created well known client tenant parent");
            }

            if (existingServiceTenantParent == null)
            {
                await tenantStore.CreateWellKnownChildTenantAsync(
                    tenantStore.Root.Id,
                    WellKnownTenantIds.ServiceTenantParentGuid,
                    TenantNames.ServiceTenantParent).ConfigureAwait(false);

                logger?.LogInformation("Created well known service tenant parent");
            }
        }