using Fitness_core_BE.Interfaces; using Fitness_core_BE.Models; using Microsoft.EntityFrameworkCore; namespace Fitness_core_BE.Repos { public class StaffRepository : IStaffRepository { private readonly TenantDbContext _context; private readonly CentralDbContext _central; public StaffRepository(TenantDbContext context, CentralDbContext central) { _context = context; _central = central; } // Staff.User/Staff.Client are central-bucket entities (not a real relationship once // physically split) -- these load the central User rows separately and attach them // in memory onto the already-fetched Staff objects, so callers reading staff.User.* // still see the right data without needing to change. private async Task AttachUsersAsync(IEnumerable staffList) { var userIds = staffList.Select(s => s.FKUserId).Distinct().ToList(); if (userIds.Count == 0) return; var users = await _central.Users .AsNoTracking() .Where(u => userIds.Contains(u.UserId)) .ToDictionaryAsync(u => u.UserId); foreach (var staff in staffList) { if (users.TryGetValue(staff.FKUserId, out var user)) staff.User = user; } } public async Task> GetAllStaff(bool includeInactive = false) { var query = _context.Staff .Include(s => s.GymRole) .AsQueryable(); if (!includeInactive) { query = query.Where(s => s.IsActive); } var staffList = await query.ToListAsync(); await AttachUsersAsync(staffList); return staffList.OrderBy(s => s.User?.UserName).ToList(); } public async Task GetStaffById(Guid staffId) { var staff = await _context.Staff .Include(s => s.GymRole) .FirstOrDefaultAsync(s => s.StaffId == staffId); if (staff != null) await AttachUsersAsync(new[] { staff }); return staff; } public async Task> GetStaffByClientId(Guid clientId, bool includeInactive = false) { var query = _context.Staff .Include(s => s.GymRole) .Where(s => s.FKClientId == clientId); if (!includeInactive) { query = query.Where(s => s.IsActive); } var staffList = await query.ToListAsync(); await AttachUsersAsync(staffList); return staffList.OrderBy(s => s.User?.UserName).ToList(); } public async Task CreateStaff(Staff staff, User user) { // Create User first (central) user.UserId = Guid.NewGuid(); user.CreateDate = DateTime.UtcNow; user.IsActive = true; _central.Users.Add(user); // Create Staff (tenant) staff.StaffId = Guid.NewGuid(); staff.FKUserId = user.UserId; staff.CreatedAt = DateTime.UtcNow; staff.HireDate = staff.HireDate == default ? DateTime.UtcNow : staff.HireDate; staff.IsActive = true; // Verify Client exists (central) var clientExists = await _central.Client.AnyAsync(c => c.ClientId == staff.FKClientId); if (!clientExists) { throw new Exception("Client not found"); } _context.Staff.Add(staff); return staff; } public async Task UpdateStaff(Staff staff) { var existingStaff = await _context.Staff .FirstOrDefaultAsync(s => s.StaffId == staff.StaffId); if (existingStaff == null) { throw new Exception("Staff not found"); } // Update User fields if provided -- User lives in the central DB. if (staff.User != null && (!string.IsNullOrEmpty(staff.User.UserName) || !string.IsNullOrEmpty(staff.User.Email) || !string.IsNullOrEmpty(staff.User.PhoneNumber))) { var centralUser = await _central.Users.FirstOrDefaultAsync(u => u.UserId == existingStaff.FKUserId); if (centralUser != null) { if (!string.IsNullOrEmpty(staff.User.UserName)) centralUser.UserName = staff.User.UserName; if (!string.IsNullOrEmpty(staff.User.Email)) centralUser.Email = staff.User.Email; if (!string.IsNullOrEmpty(staff.User.PhoneNumber)) centralUser.PhoneNumber = staff.User.PhoneNumber; existingStaff.User = centralUser; } } // Update Staff fields if provided if (!string.IsNullOrEmpty(staff.StaffPosition)) { existingStaff.StaffPosition = staff.StaffPosition; } if (!string.IsNullOrEmpty(staff.Department)) { existingStaff.Department = staff.Department; } // Update IsActive explicitly existingStaff.IsActive = staff.IsActive; return existingStaff; } public async Task DeleteStaff(Guid staffId, bool softDelete = true) { var staff = await _context.Staff.FindAsync(staffId); if (staff == null) { return false; } if (softDelete) { // Soft delete: just mark as inactive staff.IsActive = false; } else { // Hard delete: remove from database _context.Staff.Remove(staff); } return true; } public async Task GetActivePayRateAsync(Guid staffId) { return await _context.StaffPayRates .Where(r => r.FKStaffId == staffId && r.IsActive) .OrderByDescending(r => r.EffectiveFrom) .FirstOrDefaultAsync(); } public async Task AddPayRateAsync(StaffPayRate rate) { await _context.StaffPayRates.AddAsync(rate); return rate; } public async Task> GetPaymentsAsync(Guid staffId) { return await _context.StaffPayments .AsNoTracking() .Where(p => p.FKStaffId == staffId) .OrderByDescending(p => p.PaidAt) .ToListAsync(); } public async Task AddPaymentAsync(StaffPayment payment) { _context.StaffPayments.Add(payment); return payment; } } }