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 ILogger _logger; public CharactersController(CharacterStore characters, ILogger logger) { _characters = characters; _logger = logger; } [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 }, VisionRadius = 3, 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); } [HttpGet("{id}/visible-locations")] [Authorize(Roles = "USER,SUPER")] public async Task VisibleLocations(string id) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (string.IsNullOrWhiteSpace(userId)) return Unauthorized(); var allowAnyOwner = User.IsInRole("SUPER"); var character = await _characters.GetForOwnerByIdAsync(id, userId, allowAnyOwner); if (character is null) return NotFound(); _logger.LogInformation( "Visible locations requested for character {CharacterId} at ({X},{Y}) radius {VisionRadius} by user {UserId}", character.Id, character.Coord.X, character.Coord.Y, character.VisionRadius, userId ); var generation = await _characters.GetOrCreateVisibleLocationsAsync(character); var locations = generation.Locations; _logger.LogInformation( "Visible locations resolved for character {CharacterId}: generated {GeneratedCount}, returned {ReturnedCount}", character.Id, generation.GeneratedCount, locations.Count ); return Ok(locations); } [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"); } }