using System.ComponentModel.DataAnnotations; using System.Security.Claims; using line_gestao_api.Dtos; using line_gestao_api.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace line_gestao_api.Controllers; [ApiController] [Route("api/profile")] [Authorize] public class ProfileController : ControllerBase { private static readonly EmailAddressAttribute EmailValidator = new(); private readonly UserManager _userManager; public ProfileController(UserManager userManager) { _userManager = userManager; } [HttpGet("me")] public async Task> GetMe() { var user = await GetAuthenticatedUserAsync(); if (user == null) { return Unauthorized(new { message = "Usuário não autenticado." }); } return Ok(ToProfileDto(user)); } [HttpPatch] public async Task> UpdateProfile([FromBody] UpdateProfileRequest req) { var user = await GetAuthenticatedUserAsync(); if (user == null) { return Unauthorized(new { message = "Usuário não autenticado." }); } var errors = await ValidateProfileUpdateAsync(user.Id, req); if (errors.Count > 0) { return BadRequest(new ValidationErrorResponse { Errors = errors }); } var nome = req.Nome.Trim(); var email = req.Email.Trim().ToLowerInvariant(); user.Name = nome; if (!string.Equals(user.Email, email, StringComparison.OrdinalIgnoreCase)) { var setEmailResult = await _userManager.SetEmailAsync(user, email); if (!setEmailResult.Succeeded) { return BadRequest(ToValidationErrorResponse("email", setEmailResult.Errors)); } var setUserNameResult = await _userManager.SetUserNameAsync(user, email); if (!setUserNameResult.Succeeded) { return BadRequest(ToValidationErrorResponse("email", setUserNameResult.Errors)); } } var updateResult = await _userManager.UpdateAsync(user); if (!updateResult.Succeeded) { return BadRequest(ToValidationErrorResponse("perfil", updateResult.Errors)); } return Ok(ToProfileDto(user)); } [HttpPost("change-password")] public async Task ChangePassword([FromBody] ChangeMyPasswordRequest req) { var errors = ValidatePasswordChange(req); if (errors.Count > 0) { return BadRequest(new ValidationErrorResponse { Errors = errors }); } var user = await GetAuthenticatedUserAsync(); if (user == null) { return Unauthorized(new { message = "Usuário não autenticado." }); } var result = await _userManager.ChangePasswordAsync(user, req.CredencialAtual, req.NovaCredencial); if (!result.Succeeded) { return BadRequest(MapPasswordChangeErrors(result.Errors)); } return NoContent(); } private async Task GetAuthenticatedUserAsync() { var userIdRaw = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub"); if (!Guid.TryParse(userIdRaw, out var userId)) { return null; } return await _userManager.Users .FirstOrDefaultAsync(u => u.Id == userId && u.IsActive); } private async Task> ValidateProfileUpdateAsync(Guid userId, UpdateProfileRequest req) { var errors = new List(); if (string.IsNullOrWhiteSpace(req.Nome) || req.Nome.Trim().Length < 2) { errors.Add(new ValidationErrorDto { Field = "nome", Message = "Nome é obrigatório e deve ter pelo menos 2 caracteres." }); } if (string.IsNullOrWhiteSpace(req.Email)) { errors.Add(new ValidationErrorDto { Field = "email", Message = "Email é obrigatório." }); } else { var email = req.Email.Trim().ToLowerInvariant(); if (!EmailValidator.IsValid(email)) { errors.Add(new ValidationErrorDto { Field = "email", Message = "Email inválido." }); } else { var normalized = _userManager.NormalizeEmail(email); var exists = await _userManager.Users .AnyAsync(u => u.Id != userId && u.NormalizedEmail == normalized); if (exists) { errors.Add(new ValidationErrorDto { Field = "email", Message = "E-mail já cadastrado." }); } } } return errors; } private static List ValidatePasswordChange(ChangeMyPasswordRequest req) { var errors = new List(); if (string.IsNullOrWhiteSpace(req.CredencialAtual)) { errors.Add(new ValidationErrorDto { Field = "credencialAtual", Message = "Credencial atual é obrigatória." }); } if (string.IsNullOrWhiteSpace(req.NovaCredencial)) { errors.Add(new ValidationErrorDto { Field = "novaCredencial", Message = "Nova credencial é obrigatória." }); } else if (req.NovaCredencial.Length < 8) { errors.Add(new ValidationErrorDto { Field = "novaCredencial", Message = "Nova credencial deve ter pelo menos 8 caracteres." }); } if (string.IsNullOrWhiteSpace(req.ConfirmarNovaCredencial)) { errors.Add(new ValidationErrorDto { Field = "confirmarNovaCredencial", Message = "Confirmação da nova credencial é obrigatória." }); } else if (!string.Equals(req.NovaCredencial, req.ConfirmarNovaCredencial, StringComparison.Ordinal)) { errors.Add(new ValidationErrorDto { Field = "confirmarNovaCredencial", Message = "Confirmação da nova credencial inválida." }); } return errors; } private static ValidationErrorResponse ToValidationErrorResponse(string field, IEnumerable identityErrors) { return new ValidationErrorResponse { Errors = identityErrors.Select(e => new ValidationErrorDto { Field = field, Message = e.Description }).ToList() }; } private static ValidationErrorResponse MapPasswordChangeErrors(IEnumerable identityErrors) { var errors = identityErrors.Select(e => { var field = string.Equals(e.Code, "PasswordMismatch", StringComparison.OrdinalIgnoreCase) ? "credencialAtual" : "novaCredencial"; var message = string.Equals(e.Code, "PasswordMismatch", StringComparison.OrdinalIgnoreCase) ? "Credencial atual inválida." : e.Description; return new ValidationErrorDto { Field = field, Message = message }; }).ToList(); return new ValidationErrorResponse { Errors = errors }; } private static ProfileMeDto ToProfileDto(ApplicationUser user) { return new ProfileMeDto { Id = user.Id, Nome = user.Name, Email = user.Email ?? string.Empty }; } }