// Attempts to invoke the template.
        // Warning: The _commandInput cannot be assumed to be in a state that is parsed for the template being invoked.
        //      So be sure to only get template-agnostic information from it. Anything specific to the template must be gotten from the ITemplateMatchInfo
        //      Or do a reparse if necessary (currently occurs in one error case).
        private async Task <CreationResultStatus> CreateTemplateAsync(ITemplateMatchInfo templateMatchDetails)
        {
            ITemplateInfo template = templateMatchDetails.Info;

            char[] invalidChars = Path.GetInvalidFileNameChars();

            if (_commandInput?.Name != null && _commandInput.Name.IndexOfAny(invalidChars) > -1)
            {
                string printableChars    = string.Join(", ", invalidChars.Where(x => !char.IsControl(x)).Select(x => $"'{x}'"));
                string nonPrintableChars = string.Join(", ", invalidChars.Where(char.IsControl).Select(x => $"char({(int)x})"));
                Reporter.Error.WriteLine(string.Format(LocalizableStrings.InvalidNameParameter, printableChars, nonPrintableChars).Bold().Red());
                return(CreationResultStatus.CreateFailed);
            }

            string fallbackName = new DirectoryInfo(
                !string.IsNullOrWhiteSpace(_commandInput.OutputPath)
                    ? _commandInput.OutputPath
                    : Directory.GetCurrentDirectory())
                                  .Name;

            if (string.IsNullOrEmpty(fallbackName) || string.Equals(fallbackName, "/", StringComparison.Ordinal))
            {   // DirectoryInfo("/").Name on *nix returns "/", as opposed to null or "".
                fallbackName = null;
            }
            // Name returns <disk letter>:\ for root disk folder on Windows - replace invalid chars
            else if (fallbackName.IndexOfAny(invalidChars) > -1)
            {
                Regex pattern = new Regex($"[{Regex.Escape(new string(invalidChars))}]");
                fallbackName = pattern.Replace(fallbackName, "");
                if (string.IsNullOrWhiteSpace(fallbackName))
                {
                    fallbackName = null;
                }
            }

            TemplateCreationResult instantiateResult;

            try
            {
                instantiateResult = await _templateCreator.InstantiateAsync(template, _commandInput.Name, fallbackName, _commandInput.OutputPath,
                                                                            templateMatchDetails.GetValidTemplateParameters(), _commandInput.SkipUpdateCheck, _commandInput.IsForceFlagSpecified,
                                                                            _commandInput.BaselineName, _commandInput.IsDryRun)
                                    .ConfigureAwait(false);
            }
            catch (ContentGenerationException cx)
            {
                Reporter.Error.WriteLine(cx.Message.Bold().Red());
                if (cx.InnerException != null)
                {
                    Reporter.Error.WriteLine(cx.InnerException.Message.Bold().Red());
                }

                return(CreationResultStatus.CreateFailed);
            }
            catch (TemplateAuthoringException tae)
            {
                Reporter.Error.WriteLine(tae.Message.Bold().Red());
                return(CreationResultStatus.CreateFailed);
            }

            string resultTemplateName = string.IsNullOrEmpty(instantiateResult.TemplateFullName) ? _commandInput.TemplateName : instantiateResult.TemplateFullName;

            switch (instantiateResult.Status)
            {
            case CreationResultStatus.Success:
                if (!_commandInput.IsDryRun)
                {
                    Reporter.Output.WriteLine(string.Format(LocalizableStrings.CreateSuccessful, resultTemplateName));
                }
                else
                {
                    Reporter.Output.WriteLine(LocalizableStrings.FileActionsWouldHaveBeenTaken);
                    foreach (IFileChange change in instantiateResult.CreationEffects.FileChanges)
                    {
                        Reporter.Output.WriteLine($"  {change.ChangeKind}: {change.TargetRelativePath}");
                    }
                }

                if (!string.IsNullOrEmpty(template.ThirdPartyNotices))
                {
                    Reporter.Output.WriteLine(string.Format(LocalizableStrings.ThirdPartyNotices, template.ThirdPartyNotices));
                }

                HandlePostActions(instantiateResult);
                break;

            case CreationResultStatus.CreateFailed:
                Reporter.Error.WriteLine(string.Format(LocalizableStrings.CreateFailed, resultTemplateName, instantiateResult.Message).Bold().Red());
                break;

            case CreationResultStatus.MissingMandatoryParam:
                if (string.Equals(instantiateResult.Message, "--name", StringComparison.Ordinal))
                {
                    Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingRequiredParameter, instantiateResult.Message, resultTemplateName).Bold().Red());
                }
                else
                {
                    // TODO: rework to avoid having to reparse.
                    // The canonical info could be in the ITemplateMatchInfo, but currently isn't.
                    TemplateResolver.ParseTemplateArgs(template, _hostDataLoader, _commandInput);

                    IReadOnlyList <string> missingParamNamesCanonical = instantiateResult.Message.Split(new[] { ',' })
                                                                        .Select(x => _commandInput.VariantsForCanonical(x.Trim())
                                                                                .DefaultIfEmpty(x.Trim()).First())
                                                                        .ToList();
                    string fixedMessage = string.Join(", ", missingParamNamesCanonical);
                    Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingRequiredParameter, fixedMessage, resultTemplateName).Bold().Red());
                }
                break;

            case CreationResultStatus.OperationNotSpecified:
                break;

            case CreationResultStatus.NotFound:
                Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingTemplateContentDetected, _commandName).Bold().Red());
                break;

            case CreationResultStatus.InvalidParamValues:
                TemplateUsageInformation?usageInformation = TemplateUsageHelp.GetTemplateUsageInformation(template, _environment, _commandInput, _hostDataLoader, _templateCreator);

                if (usageInformation != null)
                {
                    string invalidParamsError = InvalidParameterInfo.InvalidParameterListToString(usageInformation.Value.InvalidParameters);
                    Reporter.Error.WriteLine(invalidParamsError.Bold().Red());
                    Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunHelpForInformationAboutAcceptedParameters, $"{_commandName} {_commandInput.TemplateName}").Bold().Red());
                }
                else
                {
                    Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingTemplateContentDetected, _commandName).Bold().Red());
                    return(CreationResultStatus.NotFound);
                }
                break;

            default:
                break;
            }

            return(instantiateResult.Status);
        }
        internal static TemplateUsageInformation?GetTemplateUsageInformation(ITemplateInfo templateInfo, IEngineEnvironmentSettings environmentSettings, INewCommandInput commandInput, IHostSpecificDataLoader hostDataLoader, TemplateCreator templateCreator)
        {
            IParameterSet          allParams;
            IReadOnlyList <string> userParamsWithInvalidValues;
            HashSet <string>       userParamsWithDefaultValues;
            bool hasPostActionScriptRunner;

            ITemplate?template = environmentSettings.SettingsLoader.LoadTemplate(templateInfo, commandInput.BaselineName);

            if (template == null)
            {
                return(null);
            }

            TemplateResolver.ParseTemplateArgs(templateInfo, hostDataLoader, commandInput);
            allParams = templateCreator.SetupDefaultParamValuesFromTemplateAndHost(template, template.DefaultName ?? "testName", out IReadOnlyList <string> defaultParamsWithInvalidValues);
            templateCreator.ResolveUserParameters(template, allParams, commandInput.InputTemplateParams, out userParamsWithInvalidValues);
            hasPostActionScriptRunner = CheckIfTemplateHasScriptRunningPostActions(template, environmentSettings, commandInput, templateCreator);
            templateCreator.ReleaseMountPoints(template);

            List <InvalidParameterInfo> invalidParameters = new List <InvalidParameterInfo>();

            if (userParamsWithInvalidValues.Any())
            {
                // Lookup the input param formats - userParamsWithInvalidValues has canonical.
                foreach (string canonical in userParamsWithInvalidValues)
                {
                    commandInput.InputTemplateParams.TryGetValue(canonical, out string specifiedValue);
                    string inputFormat = commandInput.TemplateParamInputFormat(canonical);
                    InvalidParameterInfo invalidParam = new InvalidParameterInfo(InvalidParameterInfo.Kind.InvalidParameterValue, inputFormat, specifiedValue, canonical);
                    invalidParameters.Add(invalidParam);
                }
            }

            if (templateCreator.AnyParametersWithInvalidDefaultsUnresolved(defaultParamsWithInvalidValues, userParamsWithInvalidValues, commandInput.InputTemplateParams, out IReadOnlyList <string> defaultsWithUnresolvedInvalidValues))
            {
                IParameterSet templateParams = template.Generator.GetParametersForTemplate(environmentSettings, template);

                foreach (string defaultParamName in defaultsWithUnresolvedInvalidValues)
                {
                    ITemplateParameter param = templateParams.ParameterDefinitions.FirstOrDefault(x => string.Equals(x.Name, defaultParamName, StringComparison.Ordinal));

                    if (param != null)
                    {
                        // Get the best input format available.
                        IReadOnlyList <string> inputVariants = commandInput.VariantsForCanonical(param.Name);
                        string displayName = inputVariants.FirstOrDefault(x => x.Contains(param.Name))
                                             ?? inputVariants.Aggregate("", (max, cur) => max.Length > cur.Length ? max : cur)
                                             ?? param.Name;

                        InvalidParameterInfo invalidParam = new InvalidParameterInfo(InvalidParameterInfo.Kind.InvalidDefaultValue, displayName, param.DefaultValue, displayName);
                        invalidParameters.Add(invalidParam);
                    }
                }
            }

            // get all the flags
            // get all the user input params that have the default value
            Dictionary <string, IReadOnlyList <string> > inputFlagVariants = new Dictionary <string, IReadOnlyList <string> >();

            userParamsWithDefaultValues = new HashSet <string>();
            foreach (string paramName in allParams.ParameterDefinitions.Select(x => x.Name))
            {
                inputFlagVariants[paramName] = commandInput.VariantsForCanonical(paramName);

                if (commandInput.TemplateParamHasValue(paramName) && string.IsNullOrEmpty(commandInput.TemplateParamValue(paramName)))
                {
                    userParamsWithDefaultValues.Add(paramName);
                }
            }
            IReadOnlyDictionary <string, IReadOnlyList <string> > variantsForCanonicals = inputFlagVariants;

            return(new TemplateUsageInformation
            {
                InvalidParameters = invalidParameters,
                AllParameters = allParams,
                UserParametersWithInvalidValues = userParamsWithInvalidValues,
                UserParametersWithDefaultValues = userParamsWithDefaultValues,
                VariantsForCanonicals = variantsForCanonicals,
                HasPostActionScriptRunner = hasPostActionScriptRunner
            });
        }