using Microsoft.AspNetCore.Authorization; using PowderCoating.Shared.Constants; using Microsoft.AspNetCore.Mvc; using PowderCoating.Application.Interfaces; namespace PowderCoating.Web.Controllers; [Authorize(Policy = AppConstants.Policies.SuperAdminOnly)] public class SeedDataController : Controller { private readonly ISeedDataService _seedDataService; private readonly ILogger _logger; public SeedDataController( ISeedDataService seedDataService, ILogger logger) { _seedDataService = seedDataService; _logger = logger; } /// /// Displays the Seed Data management page with a list of all companies. Seeding is intentionally NOT automatic on startup — it must be triggered manually here by a SuperAdmin to avoid polluting production databases. /// public async Task Index() { var companies = await _seedDataService.GetCompaniesAsync(); return View(companies); } /// /// Triggers system-level seeding (global data such as SuperAdmin accounts and dashboard tips). Warnings are stored in TempData as a pipe-delimited string to survive the redirect; they display on the Index view after the POST-Redirect-GET cycle. /// [HttpPost] [ValidateAntiForgeryToken] public async Task SeedSystem() { try { var result = await _seedDataService.SeedSystemDataAsync(); if (result.Success) { TempData["SuccessMessage"] = result.Message; TempData["SeedDetails"] = string.Join("|", result.Details); TempData["ItemsSeeded"] = result.ItemsSeeded; if (result.Warnings.Any()) { TempData["WarningMessage"] = $"{result.ItemsSkipped} item(s) were skipped"; TempData["SeedWarnings"] = string.Join("|", result.Warnings); } } else { TempData["ErrorMessage"] = result.Message; } } catch (Exception ex) { _logger.LogError(ex, "Error seeding system data"); TempData["ErrorMessage"] = $"An error occurred: {ex.Message}"; } return RedirectToAction(nameof(Index)); } /// /// Seeds demo data (customers, jobs, quotes, inventory, etc.) for a specific company. Warnings are capped at 30 entries in TempData to stay within the 4 KB browser cookie limit; any overflow is noted with a count and a pointer to the server logs. /// [HttpPost] [ValidateAntiForgeryToken] public async Task SeedCompany(int companyId) { try { var result = await _seedDataService.SeedCompanyDataAsync(companyId); if (result.Success) { TempData["SuccessMessage"] = result.Message; TempData["SeedDetails"] = string.Join("|", result.Details); TempData["ItemsSeeded"] = result.ItemsSeeded; if (result.Warnings.Any()) { TempData["WarningMessage"] = $"{result.ItemsSkipped} item(s) were skipped"; // Cap at 30 warnings to keep the TempData cookie under the 4 KB browser limit var displayWarnings = result.Warnings.Take(30).ToList(); if (result.Warnings.Count > 30) displayWarnings.Add($"… and {result.Warnings.Count - 30} more (see logs)"); TempData["SeedWarnings"] = string.Join("|", displayWarnings); } } else { TempData["ErrorMessage"] = result.Message; } } catch (Exception ex) { _logger.LogError(ex, "Error seeding company data"); TempData["ErrorMessage"] = $"An error occurred: {ex.Message}"; } return RedirectToAction(nameof(Index)); } /// /// Wipes all seeded data from the DEMO company and immediately re-seeds it with fresh demo data /// so all dates are current. Intended for tutorial recording resets — one click returns the demo /// company to a clean, realistic state without touching any other tenant. /// [HttpPost] [ValidateAntiForgeryToken] public async Task ResetDemoCompany() { try { var companies = await _seedDataService.GetCompaniesAsync(); var demo = companies.FirstOrDefault(c => c.CompanyCode == "DEMO"); if (demo == null) { TempData["ErrorMessage"] = "Demo company (code: DEMO) not found. Run Seed System Data first."; return RedirectToAction(nameof(Index)); } // Full wipe — ForceRemoveAll bypasses fingerprint matching so stale seed data from // previous code versions (different emails, renamed SKUs, etc.) is always cleared. var removeOptions = new RemoveSeedDataOptions { Customers = true, InventoryItems = true, Equipment = true, Catalog = true, PricingTiers = true, OperatingCosts = true, Bills = true, Expenses = true, Workers = false, // workers stay static — never deleted on reset Vendors = true, NamedOvens = true, Appointments = true, ForceRemoveAll = true, }; var removeResult = await _seedDataService.RemoveSeedDataAsync(demo.Id, removeOptions); if (!removeResult.Success) { TempData["ErrorMessage"] = $"Wipe step failed: {removeResult.Message}"; return RedirectToAction(nameof(Index)); } // Re-seed with today's dates var seedResult = await _seedDataService.SeedCompanyDataAsync(demo.Id); if (seedResult.Success) { TempData["SuccessMessage"] = $"Demo company reset complete. {seedResult.ItemsSeeded} records re-seeded with today's dates."; TempData["SeedDetails"] = string.Join("|", seedResult.Details); TempData["ItemsSeeded"] = seedResult.ItemsSeeded; if (seedResult.Warnings.Any()) { TempData["WarningMessage"] = $"{seedResult.ItemsSkipped} item(s) were skipped"; var displayWarnings = seedResult.Warnings.Take(30).ToList(); if (seedResult.Warnings.Count > 30) displayWarnings.Add($"... and {seedResult.Warnings.Count - 30} more (see logs)"); TempData["SeedWarnings"] = string.Join("|", displayWarnings); } } else { TempData["ErrorMessage"] = $"Wipe succeeded but re-seed failed: {seedResult.Message}"; } } catch (Exception ex) { _logger.LogError(ex, "Error resetting demo company"); TempData["ErrorMessage"] = $"An error occurred during demo reset: {ex.Message}"; } return RedirectToAction(nameof(Index)); } /// /// Removes previously seeded demo data from a company according to the supplied options (e.g., jobs only, or all data). Used during QA/demo resets to return a company to a clean state without a full database drop. /// [HttpPost] [ValidateAntiForgeryToken] public async Task RemoveSeedData(int companyId, RemoveSeedDataOptions options) { try { var result = await _seedDataService.RemoveSeedDataAsync(companyId, options); if (result.Success) { TempData["SuccessMessage"] = result.Message; TempData["SeedDetails"] = string.Join("|", result.Details); TempData["ItemsSeeded"] = result.ItemsSeeded; } else { TempData["ErrorMessage"] = result.Message; } } catch (Exception ex) { _logger.LogError(ex, "Error removing seed data for company {CompanyId}", companyId); TempData["ErrorMessage"] = $"An error occurred: {ex.Message}"; } return RedirectToAction(nameof(Index)); } }