using Microsoft.AspNetCore.Mvc; using PlotLine.Services; using PlotLine.ViewModels; namespace PlotLine.Controllers; public sealed class ScenesController(ISceneService scenes, IPlotService plots, IAssetService assets, ICharacterService characters, ILocationService locations) : Controller { public async Task Create(int chapterId) { var model = await scenes.GetCreateAsync(chapterId); return model is null ? NotFound() : View("Edit", model); } public async Task Edit(int id) { var model = await scenes.GetEditAsync(id); return model is null ? NotFound() : View(model); } [HttpPost] [ValidateAntiForgeryToken] public async Task Save(SceneEditViewModel model) { if (!ModelState.IsValid) { return View("Edit", model); } var sceneId = await scenes.SaveAsync(model); if (model.ReturnToTimeline && model.ReturnProjectID.HasValue) { return RedirectToAction("Index", "Timeline", new { projectId = model.ReturnProjectID.Value, bookId = model.ReturnBookID, selectedSceneId = sceneId }); } return RedirectToAction(nameof(Edit), new { id = sceneId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task Archive(int id, int chapterId) { await scenes.ArchiveAsync(id); return RedirectToAction("Details", "Chapters", new { id = chapterId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task MoveUp(int id, int chapterId) { await scenes.MoveAsync(id, "Up"); return RedirectToAction("Details", "Chapters", new { id = chapterId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task MoveDown(int id, int chapterId) { await scenes.MoveAsync(id, "Down"); return RedirectToAction("Details", "Chapters", new { id = chapterId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddThreadEvent(ThreadEventCreateViewModel model) { await plots.AddThreadEventAsync(model); if (model.ReturnProjectID.HasValue) { return RedirectToAction("Index", "Timeline", new { projectId = model.ReturnProjectID.Value, bookId = model.ReturnBookID, selectedSceneId = model.SceneID }); } return RedirectToAction(nameof(Edit), new { id = model.SceneID }); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeleteThreadEvent(int id, int sceneId, int? projectId, int? bookId) { await plots.DeleteThreadEventAsync(id); if (projectId.HasValue) { return RedirectToAction("Index", "Timeline", new { projectId = projectId.Value, bookId, selectedSceneId = sceneId }); } return RedirectToAction(nameof(Edit), new { id = sceneId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddAssetEvent(AssetEventCreateViewModel model) { await assets.AddAssetEventAsync(model); if (model.ReturnProjectID.HasValue) { return RedirectToAction("Index", "Timeline", new { projectId = model.ReturnProjectID.Value, bookId = model.ReturnBookID, selectedSceneId = model.SceneID }); } return RedirectToAction(nameof(Edit), new { id = model.SceneID }); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeleteAssetEvent(int id, int sceneId, int? projectId, int? bookId) { await assets.DeleteAssetEventAsync(id); if (projectId.HasValue) { return RedirectToAction("Index", "Timeline", new { projectId = projectId.Value, bookId, selectedSceneId = sceneId }); } return RedirectToAction(nameof(Edit), new { id = sceneId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddAssetCustodyEvent(AssetCustodyCreateViewModel model) { await assets.AddCustodyEventAsync(model); if (model.ReturnProjectID.HasValue) { return RedirectToAction("Index", "Timeline", new { projectId = model.ReturnProjectID.Value, bookId = model.ReturnBookID, selectedSceneId = model.SceneID }); } return RedirectToAction(nameof(Edit), new { id = model.SceneID }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddSceneAssetLocation(SceneAssetLocationCreateViewModel model) { await locations.SaveSceneAssetLocationAsync(model); return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeleteSceneAssetLocation(int id, int sceneId, int? projectId, int? bookId) { await locations.DeleteSceneAssetLocationAsync(id); return RedirectForScene(sceneId, projectId, bookId); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeleteAssetCustodyEvent(int id, int sceneId, int? projectId, int? bookId) { await assets.DeleteCustodyEventAsync(id); if (projectId.HasValue) { return RedirectToAction("Index", "Timeline", new { projectId = projectId.Value, bookId, selectedSceneId = sceneId }); } return RedirectToAction(nameof(Edit), new { id = sceneId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddSceneCharacter(SceneCharacterCreateViewModel model) { await characters.AddSceneCharacterAsync(model); return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeleteSceneCharacter(int id, int sceneId, int? projectId, int? bookId) { await characters.DeleteSceneCharacterAsync(id); return RedirectForScene(sceneId, projectId, bookId); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddSceneDependency(SceneDependencyCreateViewModel model) { await scenes.AddDependencyAsync(model); return RedirectForScene(model.CurrentSceneID, model.ReturnProjectID, model.ReturnBookID); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeleteSceneDependency(int id, int sceneId, int? projectId, int? bookId) { await scenes.DeleteDependencyAsync(id); return RedirectForScene(sceneId, projectId, bookId); } [HttpPost] [ValidateAntiForgeryToken] public async Task PreviewMove(SceneMoveRequestViewModel model) { var preview = await scenes.GetMovePreviewAsync(model); return preview is null ? NotFound() : View("MovePreview", preview); } [HttpPost] [ValidateAntiForgeryToken] public async Task ConfirmMove(SceneMoveRequestViewModel model) { await scenes.MoveToChapterAsync(model); return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID); } [HttpPost] [ValidateAntiForgeryToken] public async Task RenumberChapter(int chapterId, int? projectId, int? bookId, int? sceneId) { await scenes.RenumberChapterAsync(chapterId); return sceneId.HasValue ? RedirectForScene(sceneId.Value, projectId, bookId) : RedirectToAction("Details", "Chapters", new { id = chapterId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddCharacterAttributeEvent(CharacterAttributeEventCreateViewModel model) { await characters.AddAttributeEventAsync(model); return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddCharacterKnowledge(CharacterKnowledgeCreateViewModel model) { await characters.AddKnowledgeAsync(model); return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddRelationshipEvent(RelationshipEventCreateViewModel model) { await characters.AddRelationshipEventAsync(model); return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID); } private IActionResult RedirectForScene(int sceneId, int? projectId, int? bookId) { if (projectId.HasValue) { return RedirectToAction("Index", "Timeline", new { projectId = projectId.Value, bookId, selectedSceneId = sceneId }); } return RedirectToAction(nameof(Edit), new { id = sceneId }); } }