using LiteCharms.Features.MidrandBooks.AuthorBooks; using LiteCharms.Features.MidrandBooks.Authors; using LiteCharms.Features.MidrandBooks.Products; using LiteCharms.Features.MidrandBooks.Seed.Configuration; namespace LiteCharms.Features.MidrandBooks.Seed; public class ProductsSeederService(ProductService productService, AuthorService authorService, BooksService booksService, IOptions options, ILogger logger) : BackgroundService { private readonly CdnSettings cdnSettings = options.Value; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { logger.LogInformation("Product Seeding started"); if (cdnSettings.BookCovers is null || cdnSettings.BookCovers.Length == 0) { logger.LogWarning("No book covers found in CDN settings. Seeding aborted."); return; } // Initialize Bogus Faker engine var faker = new Faker(); var culture = CultureInfo.InvariantCulture; // Ensure repeatable data sets if run multiple times by anchoring the seed Randomizer.Seed = new Random(42); foreach (var bookCover in cdnSettings.BookCovers) { if (stoppingToken.IsCancellationRequested) break; // Generate beautifully mixed eclectic topics on the fly var bookTopic = faker.PickRandom( // --- Tech & IT --- "C# 12 & Modern .NET Architecture", "PostgreSQL Database Optimization", "Docker & Kubernetes in Production", "Domain-Driven Design Paradigms", "Artificial Intelligence with Python", // --- Sci-Fi & Fantasy --- "The Chronicles of the Quantum Nebula", "Legends of the Lost Cybernetic Kingdom", "Parallel Dimensions and Rogue Time Streams", "The Last Android in Neo-Johannesburg", // --- Thrillers, Mystery & Crime --- "The Midnight Code Cryptograph", "Shadows in the Highveld", "The Silent Witness of Midrand", "Deception on the 14th Floor", // --- Business, Finance & Wealth --- "Mastering the South African Tech Market", "The Modern Entrepreneur's Blueprint", "Generational Wealth and Venture Capital", "Negotiation Tactics for High-Stakes Deals", // --- Self-Help & Personal Growth --- "The Art of Relentless Focus", "Building High-Performance Habits", "The Mindfulness Guide for Software Engineers", "Unlocking Creative Flow Under Pressure" ); // Defensive Length Processing to avoid Entity Framework / Postgres string truncation crashes var rawTitle = $"{faker.Company.CatchPhrase()} with {bookTopic}"; var bookTitle = rawTitle.Length > 255 ? rawTitle[..252] + "..." : rawTitle; var rawSummary = $"A comprehensive guide to mastering {bookTopic}. Learn modern implementation techniques through real-world software engineering paradigms."; var bookSummary = rawSummary.Length > 512 ? rawSummary[..509] + "..." : rawSummary; // Generating a single concise paragraph ensures a rich text description falling safely well under 1024 var rawDescription = faker.Lorem.Paragraph(3); var bookDescription = rawDescription.Length > 1024 ? rawDescription[..1021] + "..." : rawDescription; var authorFirstName = faker.Name.FirstName(); var authorLastName = faker.Name.LastName(); var publisherCompany = faker.Company.CompanyName(); // Step 1: Add Product var productCreateResult = await productService.CreateProductAsync(new Products.Models.CreateProduct { Name = bookTitle, Summary = bookSummary, Description = bookDescription, ImageUrl = $"{cdnSettings.BaseCdn}{bookCover}", Type = ProductTypes.Book, Metadata = new Models.ProductMetadata { CopyrightInfo = $"© {DateTime.UtcNow.Year} {publisherCompany}. All rights reserved.", ManufactureDate = faker.Date.Past(3).ToString("yyyy-MM-dd", culture), Manufacturer = $"{authorFirstName} {authorLastName} / {publisherCompany}", SerialNumber = faker.Phone.PhoneNumber("978-##########") }, Categories = ["Coding", "Computers", "IT"] }, stoppingToken); if (productCreateResult.IsFailed) { logger.LogError("Failed to create product: {Error}", productCreateResult.Errors[0].Message); break; } // Step 2: Enable product so it can show on the shop var enableProductResult = await productService.UpdateProductStatusAsync(productId: productCreateResult.Value, isEnabled: true, stoppingToken); if (enableProductResult.IsFailed) { logger.LogError("Failed to enable created product: {Error}", enableProductResult.Errors[0].Message); break; } // Step 3: Create Product Price var productPriceCreateResult = await productService.CreateProductPriceAsync(productId: productCreateResult.Value, request: new Products.Models.CreateProductPrice { // Generates fair, dynamic prices in Rands between R150 and R650, snapped neatly to integers Amount = Math.Round(faker.Random.Decimal(150m, 650m), 2), Discount = 0.0m }, stoppingToken); if (productPriceCreateResult.IsFailed) { logger.LogError("Failed to create product price: {Error}", productPriceCreateResult.Errors[0].Message); break; } // Step 4: Create Author var authorCreateResult = await authorService.CreateAuthorAsync(request: new Authors.Models.CreateAuthor { Name = authorFirstName, LastName = authorLastName, Company = publisherCompany, VatNumber = faker.Random.Bool() ? faker.Phone.PhoneNumber("4#########") : "", PublisherType = faker.PickRandom(), Email = faker.Internet.Email(authorFirstName, authorLastName), Website = faker.Internet.Url(), ImageUrl = faker.Internet.Avatar(), SocialMedia = [ new Models.SocialMedia { Name = "LinkedIn", ImageUrl = "https://cdn.example.com/icons/linkedin.png", Type = SocialMediaTypes.LinkedIn, Url = $"https://linkedin.com/in/{authorFirstName.ToLower(culture)}-{authorLastName.ToLower(culture)}" }, new Models.SocialMedia { Name = "GitHub", ImageUrl = "https://cdn.example.com/icons/github.png", Type = SocialMediaTypes.GitHub, Url = $"https://github.com/tech-{authorFirstName.ToLower(culture)}" } ], Biography = $"{authorFirstName} {authorLastName} is a veteran technologist and systems architect with over a decade of domain expertise. " + faker.Lorem.Paragraph(2), ThumbnailImageUrl = null }, stoppingToken); if (authorCreateResult.IsFailed) { logger.LogError("Failed to create author: {Error}", authorCreateResult.Errors[0].Message); break; } // Step 5: Create Author-Book link (product linkage) var authorBookCreateResult = await booksService.CreateBookAsync(authorId: authorCreateResult.Value, productId: productCreateResult.Value, stoppingToken); if (authorBookCreateResult.IsFailed) { logger.LogError("Failed to create author-book linkage: {Error}", authorBookCreateResult.Errors[0].Message); break; } var enableAuthorBookResult = await booksService.UpdateBookStatusAsync(bookId: authorBookCreateResult.Value, isEnabled: true, stoppingToken); if (enableAuthorBookResult.IsFailed) { logger.LogError("Failed to enable author-book link: {Error}", enableAuthorBookResult.Errors[0].Message); break; } logger.LogInformation("Successfully seeded book product: {Title}", bookTitle); } logger.LogInformation("Product Seeding completed successfully."); } }