using Microsoft.AspNetCore.Mvc; using PlotLine.Services; using PlotLine.ViewModels; namespace PlotLine.Controllers; public sealed class ScenariosController(IScenarioService scenarios) : Controller { public async Task Index(int projectId) { var model = await scenarios.ListAsync(projectId); return model is null ? NotFound() : View(model); } public async Task Create(int projectId, int? bookId) { var model = await scenarios.GetCreateAsync(projectId, bookId); return model is null ? NotFound() : View(model); } [HttpPost] [ValidateAntiForgeryToken] public async Task Create(ScenarioCreateViewModel model) { if (string.IsNullOrWhiteSpace(model.ScenarioName)) { ModelState.AddModelError(nameof(model.ScenarioName), "Scenario name is required."); var repopulated = await scenarios.GetCreateAsync(model.ProjectID, model.BookID); model.BookOptions = repopulated?.BookOptions ?? []; return View(model); } var scenarioId = await scenarios.CreateAsync(model); return RedirectToAction(nameof(Timeline), new { id = scenarioId }); } public async Task Timeline(int id) { var model = await scenarios.GetTimelineAsync(id); return model is null ? NotFound() : View(model); } [HttpPost] [ValidateAntiForgeryToken] public async Task MoveScene(int scenarioId, int sceneId, int? anchorSceneId, int? targetChapterId, string position) { await scenarios.MoveSceneAsync(scenarioId, sceneId, anchorSceneId, targetChapterId, position); return RedirectToAction(nameof(Timeline), new { id = scenarioId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task Validate(int id) { var warningCount = await scenarios.ValidateAsync(id); TempData["ScenarioMessage"] = $"Scenario validation found {warningCount} warning{(warningCount == 1 ? "" : "s")}."; return RedirectToAction(nameof(Timeline), new { id }); } public async Task Compare(int id) { var model = await scenarios.CompareAsync(id); return model is null ? NotFound() : View(model); } [HttpPost] [ValidateAntiForgeryToken] public async Task Apply(int id, string confirmation) { if (!string.Equals(confirmation, "This will replace the current scene order with the scenario order. Continue?", StringComparison.Ordinal)) { TempData["ScenarioMessage"] = "Scenario was not applied. Use the exact confirmation text."; return RedirectToAction(nameof(Compare), new { id }); } await scenarios.ApplyAsync(id); TempData["ScenarioMessage"] = "Scenario applied to the main story order."; return RedirectToAction(nameof(Timeline), new { id }); } [HttpPost] [ValidateAntiForgeryToken] public async Task Archive(int id, int projectId) { await scenarios.ArchiveAsync(id); TempData["ArchiveMessage"] = "Scenario archived. You can restore it from Archived Items."; return RedirectToAction(nameof(Index), new { projectId }); } }