using LSFE.Domain.Contracts; using LSFE.Infrastructure.Database; using LSFE.Infrastructure.Dto; using LSFE.Infrastructure.Entities; using LSFE.Infrastructure.Entities.Auth; using LSFE.Infrastructure.Model; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; namespace LSFE.Domain.Services { public class AccountService : IAccountService { private readonly UserManager _userManager; private readonly IConfiguration _configuration; private readonly RoleManager _roleManager; private readonly LSFEDbContext _context; public AccountService(UserManager userManager, IConfiguration configuration, RoleManager roleManager , LSFEDbContext context ) { _userManager = userManager; _configuration = configuration; _roleManager = roleManager; _context = context; } public async Task AddUserToRoleAsync(string userId, string roleName) { var user = await _userManager.FindByIdAsync(userId); if (user == null) return new Response { Success = false, Message = "User not found", MessCode = 0 }; // Ensure role exists if (!await _roleManager.RoleExistsAsync(roleName)) { var roleResult = await _roleManager.CreateAsync(new Role()); if (!roleResult.Succeeded) return new Response { Success = false, Message = roleResult.ToString(), MessCode = 0 }; } await _userManager.AddToRoleAsync(user, roleName); return new Response { Success = true, Message = "Add user role successfully!", MessCode = 1 }; } public async Task AddClaimToUserAsync(string userId, Claim claim) { var user = await _userManager.FindByIdAsync(userId); if (user == null) return new Response { Success = false, Message = "User not found", MessCode = 0 }; await _userManager.AddClaimAsync(user, claim); return new Response { Success = true, Message = "Success", MessCode = 1 }; } public async Task RegisterUserAsync(RegisterUserDto dto) { try { // 🔍 1. Validate required fields if (string.IsNullOrWhiteSpace(dto.UserName) || string.IsNullOrWhiteSpace(dto.Email) || string.IsNullOrWhiteSpace(dto.Password)) { return new Response { Success = false, Message = "Username, Email, and Password are required.", MessCode = 0 }; } // 🧩 2. Check if username or email already exists var existingUserByName = await _userManager.FindByNameAsync(dto.UserName); if (existingUserByName != null) { return new Response { Success = false, Message = $"Username '{dto.UserName}' is already taken.", MessCode = 0 }; } var existingUserByEmail = await _userManager.FindByEmailAsync(dto.Email); if (existingUserByEmail != null) { return new Response { Success = false, Message = $"Email '{dto.Email}' is already registered.", MessCode = 0 }; } // 🏢 3. Optional: Validate Department if (dto.DepartmentId.HasValue) { var departmentExists = await _context.Departments .AnyAsync(d => d.DepartmentId == dto.DepartmentId && d.IsActive); if (!departmentExists) { return new Response { Success = false, Message = "Invalid Department selected.", MessCode = 0 }; } } // 🧍‍♂️ 4. Create new user var user = new AppsUsers { UserName = dto.UserName, Email = dto.Email, FullName = dto.FullName, EmployeeId = dto.EmployeeId, Company = dto.Company, DepartmentId = dto.DepartmentId, Address = dto.Address, PhoneNumber = dto.PhoneNumber, CreatedDate = DateTime.UtcNow, UpdatedDate = DateTime.UtcNow, CreatedBy = dto.UpdatedBy, UpdatedBy = dto.UpdatedBy, EmailConfirmed = true // optional: mark as verified by admin }; var createResult = await _userManager.CreateAsync(user, dto.Password); if (!createResult.Succeeded) { var errors = string.Join("; ", createResult.Errors.Select(e => e.Description)); return new Response { Success = false, Message = $"Failed to create user: {errors}", MessCode = 0 }; } // 🛡️ 5. Assign Role if provided if (!string.IsNullOrWhiteSpace(dto.RoleId)) { var role = await _roleManager.FindByIdAsync(dto.RoleId); if (role == null) { return new Response { Success = false, Message = $"Role with ID '{dto.RoleId}' does not exist.", MessCode = 0 }; } await _userManager.AddToRoleAsync(user, role.Name); // optional: add user claim for role await _userManager.AddClaimAsync(user, new Claim("RoleName", role.Name)); } return new Response { Success = true, Message = "User registered successfully!", MessCode = 1 }; } catch (Exception ex) { return new Response { Success = false, Message = $"Unexpected error: {ex.Message}", MessCode = 0 }; } } public async Task CreateToken(AppsUsers user) { var authClaims = await BuildClaims(user); var authSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(_configuration["JWT:Secret"])); var token = new JwtSecurityToken( issuer: _configuration["JWT:ValidIssuer"], audience: _configuration["JWT:ValidAudience"], expires: DateTime.UtcNow.AddMinutes(30), claims: authClaims, signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256) ); return new JwtSecurityTokenHandler().WriteToken(token); } private async Task> BuildClaims(AppsUsers user) { var roles = await _userManager.GetRolesAsync(user); var claims = new List { new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.NameIdentifier, user.Id), new Claim("FullName", user.FullName ?? ""), new Claim("Company", user.Company ?? ""), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r))); return claims; } public async Task GetUserById(string userId) { return await _userManager.Users.FirstOrDefaultAsync(u => u.Id == userId); } public async Task UpdateUserAsync(UpdateUserDto dto) { using var transaction = await _context.Database.BeginTransactionAsync(); try { var user = await _userManager.FindByIdAsync(dto.Id.ToString()); if (user == null) return new Response { Success = false, Message = "User not found.", MessCode = 0 }; // --- Update basic info --- user.Email = dto.Email; user.FullName = dto.FullName; user.EmployeeId = dto.EmployeeId; user.Company = dto.Company; user.DepartmentId = dto.DepartmentId; user.Address = dto.Address; user.PhoneNumber = dto.PhoneNumber; user.UpdatedBy = dto.UpdatedBy; user.UpdatedDate = DateTime.UtcNow; // --- Update password (if provided) --- if (!string.IsNullOrWhiteSpace(dto.Password)) { var passwordHasher = new PasswordHasher(); user.PasswordHash = passwordHasher.HashPassword(user, dto.Password); } var updateResult = await _userManager.UpdateAsync(user); if (!updateResult.Succeeded) return new Response { Success = false, Message = string.Join(", ", updateResult.Errors.Select(e => e.Description)), MessCode = 0 }; // --- Update roles --- if (!string.IsNullOrEmpty(dto.RoleName)) { var currentRoles = await _userManager.GetRolesAsync(user); if (currentRoles.Any()) await _userManager.RemoveFromRolesAsync(user, currentRoles); if (await _roleManager.RoleExistsAsync(dto.RoleName)) await _userManager.AddToRoleAsync(user, dto.RoleName); else return new Response { Success = false, Message = $"Role '{dto.RoleName}' not found.", MessCode = 0 }; } // --- Update claims --- if (!string.IsNullOrEmpty(dto.RoleName)) { var existingClaims = await _userManager.GetClaimsAsync(user); var existingRoleClaim = existingClaims.FirstOrDefault(c => c.Type == "Role"); if (existingRoleClaim != null) await _userManager.RemoveClaimAsync(user, existingRoleClaim); await _userManager.AddClaimAsync(user, new Claim("Role", dto.RoleName)); } await transaction.CommitAsync(); return new Response { Success = true, Message = "User updated successfully!", MessCode = 1 }; } catch (Exception ex) { await transaction.RollbackAsync(); return new Response { Success = false, Message = $"Updating user failed: {ex.Message}", MessCode = 0 }; } } public async Task> GetUsers(string company) { try { var users = await _userManager.Users .Where(u=>u.Company== company) .Include(u => u.Department) .ToListAsync(); var roles = await _roleManager.Roles.ToListAsync(); // cache all roles once var result = new List(); foreach (var user in users) { var roleNames = await _userManager.GetRolesAsync(user); // Map role names to RoleId + RoleName var userRoles = roles .Where(r => roleNames.Contains(r.Name)) .Select(r => new RoleDto { RoleId = r.Id, RoleName = r.Name! }) .ToList(); result.Add(new UserWithRoleDto { Id = user.Id, EmployeeId = user.EmployeeId, Company = user.Company, Address = user.Address, UpdatedDate = user.UpdatedDate, UpdatedBy = user.UpdatedBy, Email = user.Email, FullName = user.FullName, RoleName = userRoles[0].RoleName, CreatedBy = user.CreatedBy, CreatedDate = user.CreatedDate, UserName = user.UserName, DepartmentId = user.DepartmentId, RoleId = userRoles[0].RoleId, Department = user.Department, AccessFailedCount = user.AccessFailedCount, PhoneNumber = user.PhoneNumber, LockoutEnd = user.LockoutEnd, LockoutEnabled = user.LockoutEnabled, UserRoles = userRoles }); } return result; } catch (Exception ex) { ex.ToString(); throw; } } public async Task PostPutDepartmentAsync(Departments dto) { var existing = await _context.Departments .FirstOrDefaultAsync(d => d.DepartmentId == dto.DepartmentId); if (existing == null) { var duplicate = await _context.Departments .AnyAsync(d => d.DepartmentName == dto.DepartmentName || d.DepartmentCode == dto.DepartmentCode); if (duplicate) { return new Response { Success = false, Message = "Department name or code already exists.", MessCode = 0 }; } _context.Departments.Add(dto); await _context.SaveChangesAsync(); return new Response { Success = true, Message = "Department created successfully.", MessCode = 1 }; } else { existing.DepartmentName = dto.DepartmentName; existing.DepartmentCode = dto.DepartmentCode; existing.IsActive = dto.IsActive; _context.Departments.Update(existing); await _context.SaveChangesAsync(); return new Response { Success = true, Message = "Department updated successfully.", MessCode = 1 }; } } public async Task> GetDepartment() { return await _context.Departments.ToListAsync(); } public async Task> GetUserRole() { return await _roleManager.Roles .Select(r => new RoleDto { RoleId = r.Id, RoleName = r.Name }) .ToListAsync(); } } }