using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Identity; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using TaskTrackingSystem.Database; using TaskTrackingSystem.Database.AppDbContextModels; using TaskTrackingSystem.Shared; using TaskTrackingSystem.Shared.Models.User; using TaskTrackingSystem.WebApi.Infrastructure; namespace TaskTrackingSystem.WebApi.Features.User { public class UserService { private readonly AppDbContext _db; private readonly IPasswordHasher _passwordHasher; private readonly Infrastructure.AuditLogService _auditLog; public UserService(AppDbContext db, IPasswordHasher passwordHasher, Infrastructure.AuditLogService auditLog) { _db = db; _passwordHasher = passwordHasher; _auditLog = auditLog; } public async Task> GetAllUsersAsync() { return await _db.Users .Where(u => !u.IsDeleted) .Select(u => new UserDto { Id = u.Id, Username = u.Username, FirstName = u.FirstName, LastName = u.LastName, Email = u.Email, Phone = u.Phone, RoleId = u.RoleId, IsActive = u.IsActive, CreatedAt = u.CreatedAt ?? DateTime.UtcNow }).ToListAsync(); } public async Task> GetPagedUsersAsync(string? search, long? roleId, string? status, int page, int pageSize) { var query = _db.Users.Where(u => !u.IsDeleted); if (!string.IsNullOrWhiteSpace(search)) { var searchTerm = search.Trim().ToLower(); query = query.Where(u => (u.FirstName != null && u.FirstName.ToLower().Contains(searchTerm)) || (u.LastName != null && u.LastName.ToLower().Contains(searchTerm)) || (u.Username != null && u.Username.ToLower().Contains(searchTerm)) || (u.Email != null && u.Email.ToLower().Contains(searchTerm))); } if (roleId.HasValue && roleId.Value > 0) { query = query.Where(u => u.RoleId == roleId.Value); } if (string.Equals(status, "active", StringComparison.OrdinalIgnoreCase)) { query = query.Where(u => u.IsActive); } else if (string.Equals(status, "inactive", StringComparison.OrdinalIgnoreCase)) { query = query.Where(u => !u.IsActive); } return await query .OrderBy(u => u.FirstName) .ThenBy(u => u.LastName) .ThenBy(u => u.Username) .Select(u => new UserDto { Id = u.Id, Username = u.Username, FirstName = u.FirstName, LastName = u.LastName, Email = u.Email, Phone = u.Phone, RoleId = u.RoleId, IsActive = u.IsActive, CreatedAt = u.CreatedAt ?? DateTime.UtcNow }) .ToPagedResultAsync(page, pageSize); } public async Task GetUserByIdAsync(long id) { var user = await _db.Users .FirstOrDefaultAsync(u => u.Id == id && !u.IsDeleted); if (user == null) return null; return new UserDto { Id = user.Id, Username = user.Username, FirstName = user.FirstName, LastName = user.LastName, Email = user.Email, Phone = user.Phone, RoleId = user.RoleId, IsActive = user.IsActive, CreatedAt = user.CreatedAt ?? DateTime.UtcNow }; } public async Task> CreateUserAsync(CreateUserDto dto, long? currentUserId = null) { if (string.IsNullOrWhiteSpace(dto.Username) || string.IsNullOrWhiteSpace(dto.FirstName) || string.IsNullOrWhiteSpace(dto.LastName) || string.IsNullOrWhiteSpace(dto.Email)) { return Result.Failure(ResultMessages.FillAllFields, 400); } var usernameExists = await _db.Users.AnyAsync(u => u.Username == dto.Username && !u.IsDeleted); if (usernameExists) { return Result.Failure("Username is already taken.", 400); } var emailExists = await _db.Users.AnyAsync(u => u.Email == dto.Email && !u.IsDeleted); if (emailExists) { return Result.Failure("Email is already registered.", 400); } var user = new TaskTrackingSystem.Database.AppDbContextModels.User { Username = dto.Username, FirstName = dto.FirstName, LastName = dto.LastName, Email = dto.Email, PasswordHash = string.Empty, Phone = dto.Phone, RoleId = dto.RoleId, IsActive = true, CreatedAt = DateTime.UtcNow, CreatedBy = currentUserId }; user.PasswordHash = _passwordHasher.HashPassword(user, dto.Password); _db.Users.Add(user); await _db.SaveChangesAsync(); var roleName = await _db.Roles.Where(r => r.Id == user.RoleId).Select(r => r.Name).FirstOrDefaultAsync() ?? "Unknown"; await _auditLog.LogAsync("Create", "User", $"Created user '{user.Username}' ({user.FirstName} {user.LastName}, {user.Email}) with role '{roleName}'"); var resultDto = new UserDto { Id = user.Id, Username = user.Username, FirstName = user.FirstName, LastName = user.LastName, Email = user.Email, Phone = user.Phone, RoleId = user.RoleId, IsActive = user.IsActive, CreatedAt = user.CreatedAt ?? DateTime.UtcNow }; return Result.Success(resultDto, 201); } public async Task UpdateUserAsync(long id, UpdateUserDto dto, long? currentUserId = null) { if (string.IsNullOrWhiteSpace(dto.Username) || string.IsNullOrWhiteSpace(dto.FirstName) || string.IsNullOrWhiteSpace(dto.LastName)) { return Result.Failure(ResultMessages.FillAllFields, 400); } var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == id && !u.IsDeleted); if (user == null) return Result.Failure(ResultMessages.UserNotFound(id), 404); var usernameExists = await _db.Users.AnyAsync(u => u.Username == dto.Username && u.Id != id && !u.IsDeleted); if (usernameExists) { return Result.Failure("Username is already taken by another user.", 400); } user.Username = dto.Username; user.FirstName = dto.FirstName; user.LastName = dto.LastName; user.Phone = dto.Phone; user.RoleId = dto.RoleId; user.IsActive = dto.IsActive; user.UpdatedAt = DateTime.UtcNow; user.UpdatedBy = currentUserId; _db.Users.Update(user); await _db.SaveChangesAsync(); var roleName = await _db.Roles.Where(r => r.Id == user.RoleId).Select(r => r.Name).FirstOrDefaultAsync() ?? "Unknown"; var statusLabel = user.IsActive ? "Active" : "Inactive"; await _auditLog.LogAsync("Update", "User", $"Updated user '{user.Username}' ({user.FirstName} {user.LastName}) — Role: '{roleName}', Status: {statusLabel}"); return Result.Success(200); } public async Task SoftDeleteUserAsync(long id, long? loggedInUserId = null) { if (loggedInUserId.HasValue && loggedInUserId.Value == id) { return Result.Failure("You cannot delete your own account.", 400); } var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == id && !u.IsDeleted); if (user == null) return Result.Failure(ResultMessages.UserNotFound(id), 404); user.IsDeleted = true; _db.Users.Update(user); await _db.SaveChangesAsync(); await _auditLog.LogAsync("Delete", "User", $"Deleted user account '{user.Username}' ({user.FirstName} {user.LastName})"); return Result.Success(200); } public async Task> GetMyProjectIdsAsync(long currentUserId) { return await _db.ProjectMembers .Where(pm => pm.UserId == currentUserId) .Select(pm => pm.ProjectId) .Distinct() .ToListAsync(); } public async Task> GetTeamUserIdsAsync(List projectIds, long currentUserId) { if (projectIds == null || projectIds.Count == 0) return new List(); return await _db.ProjectMembers .Where(pm => projectIds.Contains(pm.ProjectId) && pm.UserId != currentUserId) .Select(pm => pm.UserId) .Distinct() .ToListAsync(); } } }