using System.Security.Cryptography; using Application.Auth.DTOs; using Domain.Entities; using Domain.Ports.Repositories; using Domain.ValueObjects; using MediatR; namespace Application.Auth.Commands; public record RegisterCommand( string Username, string Password, string? Name = null, string? Email = null ) : IRequest; public class RegisterCommandHandler( IUserRepository userRepository, IStudentRepository studentRepository, IPasswordService passwordService, IJwtService jwtService, IUnitOfWork unitOfWork ) : IRequestHandler { public async Task Handle(RegisterCommand request, CancellationToken ct) { // Check if username already exists if (await userRepository.ExistsAsync(request.Username, ct)) return new AuthResponse(false, Error: "El nombre de usuario ya existe"); // Validate password strength if (request.Password.Length < 6) return new AuthResponse(false, Error: "La contrasena debe tener al menos 6 caracteres"); // Create student if name and email are provided Student? student = null; if (!string.IsNullOrWhiteSpace(request.Name) && !string.IsNullOrWhiteSpace(request.Email)) { try { var email = Email.Create(request.Email); student = new Student(request.Name, email); studentRepository.Add(student); await unitOfWork.SaveChangesAsync(ct); } catch (Exception ex) { return new AuthResponse(false, Error: ex.Message); } } // Generate recovery code (12 chars alphanumeric) var recoveryCode = GenerateRecoveryCode(); var recoveryCodeHash = passwordService.HashPassword(recoveryCode); // Create user with recovery code var passwordHash = passwordService.HashPassword(request.Password); var user = User.Create( request.Username, passwordHash, recoveryCodeHash, UserRoles.Student, student?.Id ); await userRepository.AddAsync(user, ct); await unitOfWork.SaveChangesAsync(ct); // Generate token var token = jwtService.GenerateToken(user); return new AuthResponse( Success: true, Token: token, User: new UserInfo( user.Id, user.Username, user.Role, user.StudentId, student?.Name ), RecoveryCode: recoveryCode // Show only once! ); } private static string GenerateRecoveryCode() { const string chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; var bytes = RandomNumberGenerator.GetBytes(12); return new string(bytes.Select(b => chars[b % chars.Length]).ToArray()); } }