using CharacterApi.Models; using CharacterApi.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Security.Claims; namespace CharacterApi.Controllers; [ApiController] [Route("api/[controller]")] public class CharactersController : ControllerBase { private readonly CharacterStore _characters; private readonly LocationsClient _locations; public CharactersController(CharacterStore characters, LocationsClient locations) { _characters = characters; _locations = locations; } [HttpPost] [Authorize(Roles = "USER,SUPER")] public async Task Create([FromBody] CreateCharacterRequest req) { if (string.IsNullOrWhiteSpace(req.Name)) return BadRequest("Name required"); var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (string.IsNullOrWhiteSpace(userId)) return Unauthorized(); var character = new Character { OwnerUserId = userId, Name = req.Name.Trim(), Coord = new Coord { X = 0, Y = 0 }, CreatedUtc = DateTime.UtcNow }; await _characters.CreateAsync(character); return Ok(character); } [HttpGet] [Authorize(Roles = "USER,SUPER")] public async Task ListMine() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (string.IsNullOrWhiteSpace(userId)) return Unauthorized(); var characters = await _characters.GetForOwnerAsync(userId); return Ok(characters); } [HttpDelete("{id}")] [Authorize(Roles = "USER,SUPER")] public async Task Delete(string id) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (string.IsNullOrWhiteSpace(userId)) return Unauthorized(); var allowAnyOwner = User.IsInRole("SUPER"); var deleted = await _characters.DeleteForOwnerAsync(id, userId, allowAnyOwner); if (!deleted) return NotFound(); return Ok("Deleted"); } [HttpPut("{id}/move")] [Authorize(Roles = "USER,SUPER")] public async Task Move(string id, [FromBody] MoveCharacterRequest req, CancellationToken ct) { if (req?.Coord is null) return BadRequest("Coord required"); var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (string.IsNullOrWhiteSpace(userId)) return Unauthorized(); var allowAnyOwner = User.IsInRole("SUPER"); var existing = await _characters.GetForOwnerByIdAsync(id, userId, allowAnyOwner); if (existing is null) return NotFound(); var presence = await _locations.UpdatePresenceAsync(id, req.Coord, ct); if (!presence.Ok) { var message = string.IsNullOrWhiteSpace(presence.Body) ? "Location presence update failed" : presence.Body; return StatusCode((int)presence.Status, message); } var updated = await _characters.UpdateCoordAsync(id, userId, allowAnyOwner, req.Coord); if (!updated) { await _locations.UpdatePresenceAsync(id, existing.Coord, ct); return StatusCode(500, "Failed to update character coord"); } return Ok("Moved"); } }