| | | 1 | | using Chronicis.Api.Infrastructure; |
| | | 2 | | using Chronicis.Api.Models; |
| | | 3 | | using Chronicis.Api.Services; |
| | | 4 | | using Chronicis.Shared.DTOs.Quests; |
| | | 5 | | using Chronicis.Shared.Extensions; |
| | | 6 | | using Microsoft.AspNetCore.Authorization; |
| | | 7 | | using Microsoft.AspNetCore.Mvc; |
| | | 8 | | |
| | | 9 | | namespace Chronicis.Api.Controllers; |
| | | 10 | | |
| | | 11 | | /// <summary> |
| | | 12 | | /// API endpoints for Quest management. |
| | | 13 | | /// </summary> |
| | | 14 | | [ApiController] |
| | | 15 | | [Authorize] |
| | | 16 | | public class QuestsController : ControllerBase |
| | | 17 | | { |
| | | 18 | | private readonly IQuestService _questService; |
| | | 19 | | private readonly ICurrentUserService _currentUserService; |
| | | 20 | | private readonly ILogger<QuestsController> _logger; |
| | | 21 | | |
| | 0 | 22 | | public QuestsController( |
| | 0 | 23 | | IQuestService questService, |
| | 0 | 24 | | ICurrentUserService currentUserService, |
| | 0 | 25 | | ILogger<QuestsController> logger) |
| | | 26 | | { |
| | 0 | 27 | | _questService = questService; |
| | 0 | 28 | | _currentUserService = currentUserService; |
| | 0 | 29 | | _logger = logger; |
| | 0 | 30 | | } |
| | | 31 | | |
| | | 32 | | /// <summary> |
| | | 33 | | /// GET /arcs/{arcId}/quests - Get all quests for an arc. |
| | | 34 | | /// </summary> |
| | | 35 | | [HttpGet("arcs/{arcId:guid}/quests")] |
| | | 36 | | public async Task<ActionResult<List<QuestDto>>> GetQuestsByArc(Guid arcId) |
| | | 37 | | { |
| | 0 | 38 | | var user = await _currentUserService.GetRequiredUserAsync(); |
| | 0 | 39 | | _logger.LogDebug("Getting quests for arc {ArcId} for user {UserId}", arcId, user.Id); |
| | | 40 | | |
| | 0 | 41 | | var result = await _questService.GetQuestsByArcAsync(arcId, user.Id); |
| | | 42 | | |
| | 0 | 43 | | return result.Status switch |
| | 0 | 44 | | { |
| | 0 | 45 | | ServiceStatus.Success => Ok(result.Value), |
| | 0 | 46 | | ServiceStatus.NotFound => NotFound(new { error = result.ErrorMessage }), |
| | 0 | 47 | | ServiceStatus.Forbidden => StatusCode(403, new { error = result.ErrorMessage }), |
| | 0 | 48 | | _ => StatusCode(500, new { error = "An unexpected error occurred" }) |
| | 0 | 49 | | }; |
| | 0 | 50 | | } |
| | | 51 | | |
| | | 52 | | /// <summary> |
| | | 53 | | /// POST /arcs/{arcId}/quests - Create a new quest (GM only). |
| | | 54 | | /// </summary> |
| | | 55 | | [HttpPost("arcs/{arcId:guid}/quests")] |
| | | 56 | | public async Task<ActionResult<QuestDto>> CreateQuest(Guid arcId, [FromBody] QuestCreateDto dto) |
| | | 57 | | { |
| | 0 | 58 | | var user = await _currentUserService.GetRequiredUserAsync(); |
| | | 59 | | |
| | 0 | 60 | | if (dto == null) |
| | | 61 | | { |
| | 0 | 62 | | return BadRequest(new { error = "Request body is required" }); |
| | | 63 | | } |
| | | 64 | | |
| | 0 | 65 | | _logger.LogDebugSanitized("Creating quest '{Title}' in arc {ArcId} for user {UserId}", |
| | 0 | 66 | | dto.Title, arcId, user.Id); |
| | | 67 | | |
| | 0 | 68 | | var result = await _questService.CreateQuestAsync(arcId, dto, user.Id); |
| | | 69 | | |
| | 0 | 70 | | return result.Status switch |
| | 0 | 71 | | { |
| | 0 | 72 | | ServiceStatus.Success => CreatedAtAction( |
| | 0 | 73 | | nameof(GetQuest), |
| | 0 | 74 | | new { questId = result.Value!.Id }, |
| | 0 | 75 | | result.Value), |
| | 0 | 76 | | ServiceStatus.NotFound => NotFound(new { error = result.ErrorMessage }), |
| | 0 | 77 | | ServiceStatus.Forbidden => StatusCode(403, new { error = result.ErrorMessage }), |
| | 0 | 78 | | ServiceStatus.ValidationError => BadRequest(new { error = result.ErrorMessage }), |
| | 0 | 79 | | _ => StatusCode(500, new { error = "An unexpected error occurred" }) |
| | 0 | 80 | | }; |
| | 0 | 81 | | } |
| | | 82 | | |
| | | 83 | | /// <summary> |
| | | 84 | | /// GET /quests/{questId} - Get a specific quest with update count. |
| | | 85 | | /// </summary> |
| | | 86 | | [HttpGet("quests/{questId:guid}")] |
| | | 87 | | public async Task<ActionResult<QuestDto>> GetQuest(Guid questId) |
| | | 88 | | { |
| | 0 | 89 | | var user = await _currentUserService.GetRequiredUserAsync(); |
| | 0 | 90 | | _logger.LogDebug("Getting quest {QuestId} for user {UserId}", questId, user.Id); |
| | | 91 | | |
| | 0 | 92 | | var result = await _questService.GetQuestAsync(questId, user.Id); |
| | | 93 | | |
| | 0 | 94 | | return result.Status switch |
| | 0 | 95 | | { |
| | 0 | 96 | | ServiceStatus.Success => Ok(result.Value), |
| | 0 | 97 | | ServiceStatus.NotFound => NotFound(new { error = result.ErrorMessage }), |
| | 0 | 98 | | ServiceStatus.Forbidden => StatusCode(403, new { error = result.ErrorMessage }), |
| | 0 | 99 | | _ => StatusCode(500, new { error = "An unexpected error occurred" }) |
| | 0 | 100 | | }; |
| | 0 | 101 | | } |
| | | 102 | | |
| | | 103 | | /// <summary> |
| | | 104 | | /// PUT /quests/{questId} - Update a quest (GM only). Includes RowVersion concurrency check. |
| | | 105 | | /// </summary> |
| | | 106 | | [HttpPut("quests/{questId:guid}")] |
| | | 107 | | public async Task<ActionResult<QuestDto>> UpdateQuest(Guid questId, [FromBody] QuestEditDto dto) |
| | | 108 | | { |
| | 0 | 109 | | var user = await _currentUserService.GetRequiredUserAsync(); |
| | | 110 | | |
| | 0 | 111 | | if (dto == null) |
| | | 112 | | { |
| | 0 | 113 | | return BadRequest(new { error = "Request body is required" }); |
| | | 114 | | } |
| | | 115 | | |
| | 0 | 116 | | _logger.LogDebug("Updating quest {QuestId} for user {UserId}", questId, user.Id); |
| | | 117 | | |
| | 0 | 118 | | var result = await _questService.UpdateQuestAsync(questId, dto, user.Id); |
| | | 119 | | |
| | 0 | 120 | | return result.Status switch |
| | 0 | 121 | | { |
| | 0 | 122 | | ServiceStatus.Success => Ok(result.Value), |
| | 0 | 123 | | ServiceStatus.NotFound => NotFound(new { error = result.ErrorMessage }), |
| | 0 | 124 | | ServiceStatus.Forbidden => StatusCode(403, new { error = result.ErrorMessage }), |
| | 0 | 125 | | ServiceStatus.Conflict => Conflict(new |
| | 0 | 126 | | { |
| | 0 | 127 | | error = result.ErrorMessage, |
| | 0 | 128 | | currentState = result.Value // Include current QuestDto with latest RowVersion |
| | 0 | 129 | | }), |
| | 0 | 130 | | ServiceStatus.ValidationError => BadRequest(new { error = result.ErrorMessage }), |
| | 0 | 131 | | _ => StatusCode(500, new { error = "An unexpected error occurred" }) |
| | 0 | 132 | | }; |
| | 0 | 133 | | } |
| | | 134 | | |
| | | 135 | | /// <summary> |
| | | 136 | | /// DELETE /quests/{questId} - Delete a quest and all its updates (GM only). |
| | | 137 | | /// </summary> |
| | | 138 | | [HttpDelete("quests/{questId:guid}")] |
| | | 139 | | public async Task<IActionResult> DeleteQuest(Guid questId) |
| | | 140 | | { |
| | 0 | 141 | | var user = await _currentUserService.GetRequiredUserAsync(); |
| | 0 | 142 | | _logger.LogDebug("Deleting quest {QuestId} for user {UserId}", questId, user.Id); |
| | | 143 | | |
| | 0 | 144 | | var result = await _questService.DeleteQuestAsync(questId, user.Id); |
| | | 145 | | |
| | 0 | 146 | | return result.Status switch |
| | 0 | 147 | | { |
| | 0 | 148 | | ServiceStatus.Success => NoContent(), |
| | 0 | 149 | | ServiceStatus.NotFound => NotFound(new { error = result.ErrorMessage }), |
| | 0 | 150 | | ServiceStatus.Forbidden => StatusCode(403, new { error = result.ErrorMessage }), |
| | 0 | 151 | | _ => StatusCode(500, new { error = "An unexpected error occurred" }) |
| | 0 | 152 | | }; |
| | 0 | 153 | | } |
| | | 154 | | } |