using System.ComponentModel.DataAnnotations; using System.Text.Json; using API.Context; using API.DTO; using API.Validation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Model; using Services; namespace API.Controllers; [ApiController] public class TacticController(ITacticService service, IContextAccessor accessor) : ControllerBase { public record UpdateNameRequest( [StringLength(50, MinimumLength = 1)] [Name] string Name ); [HttpPut("/tactics/{tacticId:int}/name")] [Authorize] public async Task UpdateName( int tacticId, [FromBody] UpdateNameRequest req) { var userId = accessor.CurrentUserId(HttpContext); if (!await service.IsOwnerOf(userId, tacticId)) { return Unauthorized(); } var result = await service.UpdateName(tacticId, req.Name); return result ? Ok() : NotFound(); } [HttpGet("/tactics/{tacticId:int}")] [Authorize] public async Task GetTacticInfo(int tacticId) { var userId = accessor.CurrentUserId(HttpContext); if (!await service.IsOwnerOf(userId, tacticId)) { return Unauthorized(); } var result = await service.GetTactic(tacticId); return result != null ? Ok(result.ToDto()) : NotFound(); } public record GetTacticStepsTreeResponse(TacticStepDto Root); [HttpGet("/tactics/{tacticId:int}/tree")] [Authorize] public async Task GetTacticStepsRoot(int tacticId) { var userId = accessor.CurrentUserId(HttpContext); if (!await service.IsOwnerOf(userId, tacticId)) { return Unauthorized(); } var root = (await service.GetRootStep(tacticId)).ToDto(); return Ok(new GetTacticStepsTreeResponse(root)); } public record CreateNewRequest( [StringLength(50, MinimumLength = 1)] [Name] string Name, [AllowedValues("PLAIN", "HALF")] string CourtType ); public record CreateNewResponse(int Id); [HttpPost("/tactics")] [Authorize] public async Task CreateNew([FromBody] CreateNewRequest req) { var userId = accessor.CurrentUserId(HttpContext); if (!Enum.TryParse(req.CourtType, true, out var courtType)) { // unreachable if called by ASP throw new ArgumentOutOfRangeException("for req.CourtType"); } var id = await service.AddTactic(userId, req.Name, courtType); return new CreateNewResponse(id); } public record AddStepRequest(int ParentId, object Content); public record AddStepResponse(int StepId); [HttpPost("/tactics/{tacticId:int}/steps")] public async Task AddStep(int tacticId, [FromBody] AddStepRequest req) { var stepId = await service.AddTacticStep(tacticId, req.ParentId, JsonSerializer.Serialize(req.Content)); return stepId != null ? Ok(new AddStepResponse(stepId ?? 0)) : NotFound(); } [HttpGet("/tactics/{tacticId:int}/steps/{stepId:int}")] [Authorize] public async Task GetStepContent(int tacticId, int stepId) { var userId = accessor.CurrentUserId(HttpContext); if (!await service.IsOwnerOf(userId, tacticId)) { return Unauthorized(); } var json = await service.GetTacticStepContent(tacticId, stepId); return json != null ? Ok(JsonSerializer.Deserialize(json)) : NotFound(); } [HttpDelete("/tactics/{tacticId:int}/steps/{stepId:int}")] [Authorize] public async Task RemoveStep(int tacticId, int stepId) { var userId = accessor.CurrentUserId(HttpContext); if (!await service.IsOwnerOf(userId, tacticId)) { return Unauthorized(); } var found = await service.RemoveTacticStep(tacticId, stepId); return found ? Ok() : NotFound(); } public record SaveStepContentRequest(object Content); [HttpPut("/tactics/{tacticId:int}/steps/{stepId:int}")] [Authorize] public async Task SaveStepContent(int tacticId, int stepId, [FromBody] SaveStepContentRequest req) { var userId = accessor.CurrentUserId(HttpContext); if (!await service.IsOwnerOf(userId, tacticId)) { return Unauthorized(); } var found = await service.SetTacticStepContent(tacticId, stepId, JsonSerializer.Serialize(req.Content)); return found ? Ok() : NotFound(); } public record CanEditResponse(bool CanEdit); [HttpGet("/tactics/{tacticId:int}/can-edit")] [Authorize] public async Task CanEdit(int tacticId) { var userId = accessor.CurrentUserId(HttpContext); return new CanEditResponse(await service.IsOwnerOf(userId, tacticId)); } }