using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using TeleWave.Application.Admin.Users; using TeleWave.Application.Auth; using TeleWave.Application.Common.Interfaces; using TeleWave.Application.Common.Models; using TeleWave.Infrastructure.Persistence; namespace TeleWave.Infrastructure.Identity; internal sealed class IdentityService( UserManager userManager, SignInManager signInManager, RoleManager roleManager, AppDbContext dbContext ) : IIdentityService { public async Task> CreateUserAsync( string userName, string password, CancellationToken cancellationToken ) { if (await userManager.FindByNameAsync(userName) is not null) return Result.Failure(AuthErrors.UserNameTaken); var user = new AppUser { UserName = userName, CreatedAt = DateTimeOffset.UtcNow }; var createResult = await userManager.CreateAsync(user, password); if (!createResult.Succeeded) { return Result.Failure( Error.Validation( "Auth.CreateFailed", string.Join("; ", createResult.Errors.Select(e => e.Description)) ) ); } await userManager.AddToRoleAsync(user, RoleNames.User); return Result.Success(user.Id); } public async Task> ValidateCredentialsAsync( string userName, string password, CancellationToken cancellationToken ) { var user = await userManager.FindByNameAsync(userName); if (user is null) return Result.Failure(AuthErrors.InvalidCredentials); var checkResult = await signInManager.CheckPasswordSignInAsync( user, password, lockoutOnFailure: true ); if (!checkResult.Succeeded) return Result.Failure(AuthErrors.InvalidCredentials); var role = await GetPrimaryRoleAsync(user); return Result.Success(new AuthenticatedUser(user.Id, user.UserName!, role)); } public async Task GetProfileAsync( Guid userId, CancellationToken cancellationToken ) { var user = await userManager.FindByIdAsync(userId.ToString()); if (user is null) return null; var role = await GetPrimaryRoleAsync(user); var roleEntity = await roleManager.FindByNameAsync(role); return new CurrentUserProfile(user.Id, user.UserName!, roleEntity?.Id ?? Guid.Empty, role, user.IsBlocked); } public async Task ChangePasswordAsync( Guid userId, string currentPassword, string newPassword, CancellationToken cancellationToken ) { var user = await userManager.FindByIdAsync(userId.ToString()); if (user is null) return Result.Failure(AuthErrors.Unauthorized); var result = await userManager.ChangePasswordAsync(user, currentPassword, newPassword); return result.Succeeded ? Result.Success() : Result.Failure( Error.Validation( "Auth.ChangePasswordFailed", string.Join("; ", result.Errors.Select(e => e.Description)) ) ); } public async Task ChangeUserNameAsync( Guid userId, string newUserName, CancellationToken cancellationToken ) { var user = await userManager.FindByIdAsync(userId.ToString()); if (user is null) return Result.Failure(AuthErrors.Unauthorized); var existing = await userManager.FindByNameAsync(newUserName); if (existing is not null && existing.Id != userId) return Result.Failure(AuthErrors.UserNameTaken); var result = await userManager.SetUserNameAsync(user, newUserName); return result.Succeeded ? Result.Success() : Result.Failure( Error.Validation( "Auth.ChangeUserNameFailed", string.Join("; ", result.Errors.Select(e => e.Description)) ) ); } public async Task DeleteUserAsync(Guid userId, CancellationToken cancellationToken) { var user = await userManager.FindByIdAsync(userId.ToString()); if (user is null) return Result.Failure(UserErrors.NotFound); var result = await userManager.DeleteAsync(user); return result.Succeeded ? Result.Success() : Result.Failure( Error.Failure( "Users.DeleteFailed", string.Join("; ", result.Errors.Select(e => e.Description)) ) ); } public async Task BlockUserAsync(Guid userId, CancellationToken cancellationToken) { var user = await userManager.FindByIdAsync(userId.ToString()); if (user is null) return Result.Failure(UserErrors.NotFound); user.IsBlocked = true; await userManager.UpdateAsync(user); return Result.Success(); } public async Task UnblockUserAsync(Guid userId, CancellationToken cancellationToken) { var user = await userManager.FindByIdAsync(userId.ToString()); if (user is null) return Result.Failure(UserErrors.NotFound); user.IsBlocked = false; await userManager.UpdateAsync(user); return Result.Success(); } public async Task> ListUsersAsync( int page, int pageSize, string? search, Guid? roleId, bool? isBlocked, CancellationToken cancellationToken ) { var query = from user in dbContext.Users join userRole in dbContext.UserRoles on user.Id equals userRole.UserId into userRoles from userRole in userRoles.DefaultIfEmpty() join role in dbContext.Roles on userRole.RoleId equals role.Id into roles from role in roles.DefaultIfEmpty() select new { user, RoleId = (Guid?)userRole.RoleId, RoleName = role != null ? role.Name : null }; if (!string.IsNullOrWhiteSpace(search)) query = query.Where(x => x.user.UserName!.Contains(search)); if (roleId is not null) query = query.Where(x => x.RoleId == roleId); if (isBlocked is not null) query = query.Where(x => x.user.IsBlocked == isBlocked); var total = await query.CountAsync(cancellationToken); var items = await query .OrderByDescending(x => x.user.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .Select(x => new UserSummaryDto( x.user.Id, x.user.UserName!, x.RoleName ?? RoleNames.User, x.user.IsBlocked, x.user.CreatedAt )) .ToListAsync(cancellationToken); return new PagedList(items, total, page, pageSize); } public async Task GetUserAsync(Guid userId, CancellationToken cancellationToken) { var user = await userManager.FindByIdAsync(userId.ToString()); if (user is null) return null; var role = await GetPrimaryRoleAsync(user); return new UserSummaryDto(user.Id, user.UserName!, role, user.IsBlocked, user.CreatedAt); } private async Task GetPrimaryRoleAsync(AppUser user) { var roles = await userManager.GetRolesAsync(user); return roles.FirstOrDefault() ?? RoleNames.User; } }