namespace PrismaticSync.Infrastructure; /// Strongly-typed config bound from the "Sync" section of appsettings.json. public class SyncConfig { public string BaseUrl { get; set; } = "https://www.prismaticpowders.com"; public string ColorsPath { get; set; } = "/shop/powder-coating-colors"; public string ProductUrlsFile { get; set; } = "product-urls.txt"; public string OutputJsonFile { get; set; } = "prismatic_powders.json"; public string LogFile { get; set; } = "prismatic-sync.log"; /// Politeness delay between product scrapes (randomized within the range). public int MinDelaySeconds { get; set; } = 6; public int MaxDelaySeconds { get; set; } = 14; /// On a 403/block, cool down this many seconds × the consecutive-block count, then retry. public int BlockedCooldownSeconds { get; set; } = 120; /// Upper bound on a single cooldown so escalation can't run away. public int BlockedCooldownMaxSeconds { get; set; } = 600; /// How many times to cool-down-and-retry a blocked product before recording it as an error. public int BlockedMaxRetries { get; set; } = 3; /// Take a longer rest after this many products (0 disables). Eases load and looks less robotic. public int LongRestEveryProducts { get; set; } = 150; /// Length of the periodic long rest, in seconds. public int LongRestSeconds { get; set; } = 45; /// Extra settle time after a product page loads before reading it. public int PageSettleSeconds { get; set; } = 4; /// Pause after each scroll while a listing lazy-loads more items. public int ScrollWaitMs { get; set; } = 1500; /// Hard cap on scrolls per listing, as a safety stop. public int MaxScrolls { get; set; } = 400; /// Full discovery: stop a listing after this many scrolls add no new links. public int StopAfterNoNewScrolls { get; set; } = 10; /// /// Incremental discovery: stop the newest-first listing after this many consecutive scrolls /// that surfaced only already-known URLs — i.e. we've scrolled past the new products. /// public int StopAfterKnownScrolls { get; set; } = 8; /// Color filter params used by full discovery. public string[] ColorParams { get; set; } = Array.Empty(); public ImportConfig Import { get; set; } = new(); public string ColorsUrl => $"{BaseUrl.TrimEnd('/')}{ColorsPath}"; } /// Where and how to push the scraped catalog into the app. public class ImportConfig { /// Full URL of the app's token-authenticated catalog import endpoint. public string EndpointUrl { get; set; } = ""; /// Shared secret sent in the X-Import-Token header. Must match the app's config. public string Token { get; set; } = ""; /// Vendor name applied to every record on import. public string VendorName { get; set; } = "Prismatic Powders"; }