using BlueLaminate.Core.Skins; using BlueLaminate.Scraper.Skins; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.CommandLine; namespace BlueLaminate.Cli.Commands; /// /// sync-skins: load the CS2 skin catalogue and upsert it (throttled monthly). /// Presentation over ; --dry-run /// loads and prints via without touching the DB. /// internal static class SyncSkinsCommand { public static Command Build(IHost host) { var forceOption = new Option("--force") { Description = "Ignore the once-a-month throttle and sync now." }; var dryRunOption = new Option("--dry-run") { Description = "Load and print the skins without writing to the database." }; var command = new Command( "sync-skins", "Load the CS2 skin catalogue from the CSGO-API dataset and upsert it (throttled to once a month).") { forceOption, dryRunOption, }; command.SetAction((parseResult, ct) => RunAsync( host, parseResult.GetValue(forceOption), parseResult.GetValue(dryRunOption), ct)); return command; } private static async Task RunAsync(IHost host, bool force, bool dryRun, CancellationToken ct) { using var scope = host.Services.CreateScope(); if (dryRun) { return await DryRunAsync(scope.ServiceProvider, ct); } var service = scope.ServiceProvider.GetRequiredService(); var result = await service.SyncAsync(force, ct); if (result.Skipped) { Console.WriteLine( $"Skipped: skins were last synced {result.LastRanAt:u}. " + "Next run allowed one month later — pass --force to override."); } else { Console.WriteLine( $"Synced {result.Loaded} skins: {result.Inserted} inserted, " + $"{result.Updated} updated, " + $"{result.Loaded - result.Inserted - result.Updated} unchanged " + $"({result.WeaponsCreated} weapons, {result.CollectionsCreated} collections created)."); } return 0; } // Loads the catalogue and prints it without a database — no service involved. private static async Task DryRunAsync(IServiceProvider sp, CancellationToken ct) { var logger = sp.GetRequiredService().CreateLogger("BlueLaminate.Cli.SyncSkins"); var client = sp.GetRequiredService(); logger.LogInformation("Loading skin catalogue (dry run — nothing will be written)."); var skins = await client.FetchAsync(ct); logger.LogInformation("Loaded {Count} skins.", skins.Count); Console.WriteLine($"Loaded {skins.Count} skins (dry run, nothing written):"); foreach (var s in skins) { var tags = (s.StatTrakAvailable ? " ST" : "") + (s.SouvenirAvailable ? " SV" : ""); var range = s.FloatMin is not null ? $"{s.FloatMin:0.00}-{s.FloatMax:0.00}" : "—"; var sources = s.Sources.Count > 0 ? string.Join(", ", s.Sources.Select(x => x.Name)) : "—"; var idx = $"{s.DefIndex?.ToString() ?? "—"}/{s.PaintIndex?.ToString() ?? "—"}"; Console.WriteLine( $" {idx,-10} {s.WeaponName,-16} {s.Name,-24} {s.Rarity,-14} {range,-10} {sources}{tags}"); } return 0; } }