using Microsoft.Extensions.Configuration; using QuestionsApi.Models; using System.Text.Json; namespace QuestionsApi.Services; public static class DynamicBlueprintCommand { public static async Task RunAsync(string[] args, TextWriter output) { var apply = args.Contains("--apply", StringComparer.OrdinalIgnoreCase); var sourceIndex = Array.FindIndex(args, value => value.Equals("--source", StringComparison.OrdinalIgnoreCase)); var source = sourceIndex >= 0 && sourceIndex + 1 < args.Length ? args[sourceIndex + 1] : Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "content", "dynamic_questions")); if (!Directory.Exists(source)) { await output.WriteLineAsync($"Dynamic blueprint directory not found: {source}"); return 2; } var json = new JsonSerializerOptions(JsonSerializerDefaults.Web) { PropertyNameCaseInsensitive = true }; var blueprints = new List(); foreach (var file in Directory.EnumerateFiles(source, "*.json", SearchOption.AllDirectories)) blueprints.AddRange(JsonSerializer.Deserialize>(await File.ReadAllTextAsync(file), json) ?? []); var generator = new DynamicQuestionGenerator(); foreach (var blueprint in blueprints) { var error = DynamicQuestionGenerator.Validate(blueprint); if (error is not null) throw new InvalidDataException($"{blueprint.ExternalId} v{blueprint.Version}: {error}"); for (var seed = 0; seed < 1000; seed++) generator.Generate(blueprint, seed.ToString(), seed.ToString()); } await output.WriteLineAsync($"Validated {blueprints.Count} blueprints across 1,000 seeds each."); if (!apply) { await output.WriteLineAsync("Dry run only. Pass --apply to publish."); return 0; } var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: true) .AddEnvironmentVariables() .Build(); var store = new DynamicQuestionStore(new MongoDatabaseProvider(configuration)); await store.UpsertBlueprintsAsync(blueprints); await output.WriteLineAsync($"Published {blueprints.Count} immutable blueprint versions."); return 0; } }