using qtc_api.Services.BucketService; namespace qtc_api.Services.UserService { public class UserService : IUserService { private readonly IConfiguration _configuration; private readonly IBucketService _bucketService; private readonly DataContext _dataContext; private long idMax = 900000000000000000; public UserService(IConfiguration configuration, IBucketService bucketService, DataContext dataContext) { _configuration = configuration; _dataContext = dataContext; _bucketService = bucketService; } public async Task> AddUser(UserDto userReq) { var serviceResponse = new ServiceResponse(); var userList = await _dataContext.Users.ToListAsync(); var user = new User(); Random rnd = new Random(); var id = LongRandom(1, idMax, rnd); user.Id = id.ToString(); user.Username = userReq.Username; user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(userReq.Password); user.Email = userReq.Email; user.DateOfBirth = userReq.DateOfBirth; user.Role = _configuration["Jwt:DefaultUserRole"]!; user.CreatedAt = DateTime.UtcNow; // add new user tag user.Tags = ["new_user"]; try { var cUser = userList.FirstOrDefault(x => x.Email == user.Email); if (cUser != null) { serviceResponse.Success = false; serviceResponse.Message = "User with that email already exists."; return serviceResponse; } else { await _dataContext.Users.AddAsync(user); await _dataContext.SaveChangesAsync(); userList = await _dataContext.Users.ToListAsync(); if (userList.Contains(user)) { var createdUser = await _dataContext.Users.FindAsync(user.Id); serviceResponse.Success = true; serviceResponse.Data = createdUser; return serviceResponse; } else { serviceResponse.Success = false; serviceResponse.Message = "User not created."; return serviceResponse; } } } catch (Exception ex) { serviceResponse.Success = false; serviceResponse.Message = ex.Message; return serviceResponse; } } public async Task> DeleteUser(string id) { var serviceResponse = new ServiceResponse(); var user = _dataContext.Users.FirstOrDefault(x => x.Id == id)!; if (user != null) { _dataContext.Users.Remove(user); await _dataContext.SaveChangesAsync(); serviceResponse.Success = true; serviceResponse.Data = user; } else { serviceResponse.Success = false; serviceResponse.Message = "User not found."; } return serviceResponse; } public async Task>> GetAllUsers() { var serviceResponse = new ServiceResponse>(); var userList = await _dataContext.Users.ToListAsync(); var userInfoList = new List(); foreach (var user in userList) { var x = new UserInformationDto(); x.Id = user.Id; x.Username = user.Username; x.ProfilePicture = user.ProfilePicture; x.Role = user.Role; x.Bio = user.Bio; x.DateOfBirth = user.DateOfBirth; x.CurrencyAmount = user.CurrencyAmount; x.Status = user.Status; x.CreatedAt = user.CreatedAt; x.ProfileCosmeticId = user.ActiveProfileCosmetic; x.LastLogin = user.LastLogin; userInfoList.Add(x); } serviceResponse.Success = true; serviceResponse.Data = userInfoList; return serviceResponse; } public async Task>> GetAllOnlineUsers() { var serviceResponse = new ServiceResponse>(); var onlineUsers = new List(); await _dataContext.Users.ForEachAsync(user => { if (user.Status == 1 || user.Status == 2 || user.Status == 3) { var x = new UserInformationDto(); x.Id = user.Id; x.Username = user.Username; x.ProfilePicture = user.ProfilePicture; x.Role = user.Role; x.Bio = user.Bio; x.Status = user.Status; x.DateOfBirth = user.DateOfBirth; x.CreatedAt = user.CreatedAt; x.CurrencyAmount = user.CurrencyAmount; x.CreatedAt = user.CreatedAt; x.ProfileCosmeticId = user.ActiveProfileCosmetic; x.LastLogin = user.LastLogin; onlineUsers.Add(x); } }); serviceResponse.Success = true; serviceResponse.Data = onlineUsers; return serviceResponse; } public async Task> GetUserById(string id) { var serviceResponse = new ServiceResponse(); var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == id); serviceResponse.Success = true; serviceResponse.Data = user; return serviceResponse; } public async Task> GetUserInformationById(string id) { var serviceResponse = new ServiceResponse(); var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == id); if (user != null) { var dto = new UserInformationDto { Id = user.Id, Username = user.Username, ProfilePicture = user.ProfilePicture, Role = user.Role, Bio = user.Bio, DateOfBirth = user.DateOfBirth, CreatedAt = user.CreatedAt, Status = user.Status, CurrencyAmount = user.CurrencyAmount, ProfileCosmeticId = user.ActiveProfileCosmetic, LastLogin = user.LastLogin, TextStatus = user.TextStatus, Tags = user.Tags }; // always add admin tag if user is admin if (user.Role == "Admin") { List _tagList = [.. dto.Tags]; _tagList.Add("admin"); dto.Tags = [.. _tagList]; } serviceResponse.Success = true; serviceResponse.Data = dto; return serviceResponse; } else { serviceResponse.Success = false; serviceResponse.Message = "User not found."; return serviceResponse; } } public async Task> GetUserByEmail(string email) { var serviceResponse = new ServiceResponse(); var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Email == email); serviceResponse.Success = true; serviceResponse.Data = user; return serviceResponse; } public async Task> UpdateUserInfo(UserUpdateInformationDto request) { var serviceResponse = new ServiceResponse(); var dbUser = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == request.Id); if (dbUser != null) { dbUser.Username = request.Username; dbUser.Bio = request.Bio; dbUser.DateOfBirth = request.DateOfBirth; dbUser.ActiveProfileCosmetic = request.ProfileCosmeticId; dbUser.TextStatus = request.TextStatus; await _dataContext.SaveChangesAsync(); var infoDto = new UserInformationDto { Id = dbUser.Id, Username = request.Username, ProfilePicture = dbUser.ProfilePicture, Bio = request.Bio, Role = dbUser.Role, DateOfBirth = request.DateOfBirth, CreatedAt = dbUser.CreatedAt, Status = dbUser.Status, CurrencyAmount = dbUser.CurrencyAmount, ProfileCosmeticId = request.ProfileCosmeticId, TextStatus = request.TextStatus }; serviceResponse.Success = true; serviceResponse.Data = infoDto; } else { serviceResponse.Success = false; serviceResponse.Message = "User not found."; } return serviceResponse; } public async Task> UpdateUserPic(string userId, IFormFile file) { var serviceResponse = new ServiceResponse(); var userToUpdate = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == userId); var cdnPath = _configuration["GeneralConfig:CDNPath"]; if (userToUpdate == null) { serviceResponse.Success = false; serviceResponse.Message = "User Not Found."; return serviceResponse; } if (file != null && file.Length > 0) { if (!Directory.Exists(cdnPath)) Directory.CreateDirectory(cdnPath!); if (!Directory.Exists($"{cdnPath}/{userId}")) Directory.CreateDirectory($"{cdnPath}/{userId}"); if (userToUpdate.ProfilePicture != null) await _bucketService.DeleteProfileImage(userToUpdate.Id, userToUpdate.ProfilePicture); var fileName = $"{Guid.NewGuid()}.{file.FileName.Split('.')[1]}"; using var ms = new MemoryStream(); file.CopyTo(ms); var result = await _bucketService.PutProfileImage(userId, fileName, ms.ToArray()); if (result) { userToUpdate.ProfilePicture = fileName; await _dataContext.SaveChangesAsync(); serviceResponse.Success = true; serviceResponse.Data = fileName; } else { // fallback to using local cdn var filePath = Path.Combine(cdnPath ?? "./user-content", userId, fileName); using (var stream = File.Create(filePath)) { await file.CopyToAsync(stream); } userToUpdate.ProfilePicture = fileName; await _dataContext.SaveChangesAsync(); serviceResponse.Success = true; serviceResponse.Data = fileName; } } else { serviceResponse.Success = false; serviceResponse.Message = "Empty File."; } return serviceResponse; } public async Task> GetUserPic(string userId) { var serviceResponse = new ServiceResponse(); var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.Id == userId); var cdnPath = _configuration["GeneralConfig:CDNPath"]; if (user != null) { if (user.ProfilePicture != null) { var response = await _bucketService.GetProfileImageBytes(user.Id, user.ProfilePicture); if (response != null) { serviceResponse.Success = true; serviceResponse.Message = user.ProfilePicture; serviceResponse.Data = new FileContentResult(response, "image/jpeg"); } else { // try local cdn if (!Directory.Exists(cdnPath)) { serviceResponse.Success = false; serviceResponse.Message = "User Content Folder Does Not Exist Yet."; return serviceResponse; } var pic = Path.Combine(cdnPath, userId, user.ProfilePicture); if (!File.Exists(pic)) { serviceResponse.Success = false; serviceResponse.Message = "User Does Not Have A Profile Picture."; return serviceResponse; } serviceResponse.Success = true; serviceResponse.Message = user.ProfilePicture; serviceResponse.Data = new FileContentResult(File.ReadAllBytes(pic), "image/jpeg"); } } else { serviceResponse.Success = false; serviceResponse.Message = "User Does Not Have A Profile Picture."; } } else { serviceResponse.Success = false; serviceResponse.Message = "User Not Found."; } return serviceResponse; } public async Task> UpdateStatus(UserStatusDto request) { var serviceResponse = new ServiceResponse(); var user = _dataContext.Users.FirstOrDefault(x => x.Id == request.Id)!; user.Status = request.Status; if (user.Status == 0) { user.LastLogin = DateTime.UtcNow; } await _dataContext.SaveChangesAsync(); serviceResponse.Success = true; serviceResponse.Data = request; return serviceResponse; } public async Task> AddCurrencyToUser(string id, int amount, bool isSpinClaim) { var response = new ServiceResponse(); var user = _dataContext.Users.FirstOrDefault(e => e.Id == id); if (user != null) { user.CurrencyAmount += amount; if (isSpinClaim) user.LastCurrencySpin = DateTime.UtcNow; await _dataContext.SaveChangesAsync(); response.Success = true; response.Data = user.CurrencyAmount; return response; } else { response.Success = false; response.Message = "User Not Found"; return response; } } public async Task> RemoveCurrencyFromUser(string id, int amount) { var response = new ServiceResponse(); var user = _dataContext.Users.FirstOrDefault(e => e.Id == id); if (user != null) { user.CurrencyAmount -= amount; await _dataContext.SaveChangesAsync(); response.Success = true; response.Data = user.CurrencyAmount; return response; } else { response.Success = false; response.Message = "User Not Found"; return response; } } public async Task> AddStockToUser(string id, int amount) { var response = new ServiceResponse(); var user = _dataContext.Users.FirstOrDefault(e => e.Id == id); if (user != null) { user.StockAmount += amount; await _dataContext.SaveChangesAsync(); response.Success = true; response.Data = user.StockAmount; return response; } else { response.Success = false; response.Message = "User Not Found"; return response; } } public async Task> RemoveStockFromUser(string id, int amount) { var response = new ServiceResponse(); var user = _dataContext.Users.FirstOrDefault(e => e.Id == id); if (user != null) { user.StockAmount -= amount; await _dataContext.SaveChangesAsync(); response.Success = true; response.Data = user.StockAmount; return response; } else { response.Success = false; response.Message = "User Not Found"; return response; } } private long LongRandom(long min, long max, Random rnd) { long result = rnd.Next((int)(min >> 32), (int)(max >> 32)); result = result << 32; result = result | (long)rnd.Next((int)min, (int)max); return result; } } }