using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Entitlements; using Marco.Pms.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.EntityFrameworkCore; namespace MarcoBMS.Services.Helpers { public class RolesHelper { private readonly ApplicationDbContext _context; private readonly CacheUpdateHelper _cache; private readonly ILoggingService _logger; public RolesHelper(ApplicationDbContext context, CacheUpdateHelper cache, ILoggingService logger) { _context = context; _cache = cache; _logger = logger; } /// /// Retrieves a unique list of enabled feature permissions for a given employee. /// This method is optimized to use a single, composed database query. /// /// The ID of the employee. /// A distinct list of FeaturePermission objects the employee is granted. public async Task> GetFeaturePermissionByEmployeeId(Guid EmployeeId) { _logger.LogInfo("Fetching feature permissions for EmployeeId: {EmployeeId}", EmployeeId); try { // --- Step 1: Define the subquery for the employee's roles --- // This is an IQueryable, not a list. It will be composed directly into the main query // by Entity Framework, avoiding a separate database call. var employeeRoleIdsQuery = _context.EmployeeRoleMappings .Where(erm => erm.EmployeeId == EmployeeId && erm.IsEnabled == true) .Select(erm => erm.RoleId); // --- Step 2: Asynchronously update the cache in the background (Fire and Forget) --- // This task is started but not awaited. The main function continues immediately, // reducing latency. The cache will be updated eventually without blocking the user. _ = Task.Run(async () => { try { var roleIds = await employeeRoleIdsQuery.ToListAsync(); // Execute the query for the cache if (roleIds.Any()) { await _cache.AddApplicationRole(EmployeeId, roleIds); _logger.LogInfo("Successfully queued cache update for EmployeeId: {EmployeeId}", EmployeeId); } } catch (Exception ex) { // Log errors from the background task so they are not lost. _logger.LogWarning("Background cache update failed for EmployeeId {EmployeeId} : {Error}", EmployeeId, ex.Message); } }); // --- Step 3: Execute the main query to get permissions in a single database call --- // This single, efficient query gets all the required data at once. var permissions = await ( from rpm in _context.RolePermissionMappings join fp in _context.FeaturePermissions.Include(f => f.Feature) // Include related Feature data on rpm.FeaturePermissionId equals fp.Id // The 'employeeRoleIdsQuery' subquery is seamlessly integrated here by EF Core, // resulting in a SQL "IN (SELECT ...)" clause. where employeeRoleIdsQuery.Contains(rpm.ApplicationRoleId) && fp.IsEnabled == true select fp) .Distinct() // Ensures each permission is returned only once .ToListAsync(); _logger.LogInfo("Successfully retrieved {PermissionCount} unique permissions for EmployeeId: {EmployeeId}", permissions.Count, EmployeeId); return permissions; } catch (Exception ex) { _logger.LogError("An error occurred while fetching permissions for EmployeeId {EmployeeId} :{Error}", EmployeeId, ex.Message); // Depending on your application's error handling strategy, you might re-throw, // or return an empty list to prevent downstream failures. return new List(); } } public async Task> GetFeaturePermissionByRoleID1(Guid roleId) { List roleMappings = await _context.RolePermissionMappings.Where(c => c.ApplicationRoleId == roleId).Select(c => c.ApplicationRoleId).ToListAsync(); // _context.RolePermissionMappings var result = await (from rpm in _context.RolePermissionMappings.Where(c => c.ApplicationRoleId == roleId) join fp in _context.FeaturePermissions.Where(c => c.IsEnabled == true).Include(fp => fp.Feature) // Include Feature on rpm.FeaturePermissionId equals fp.Id select fp) .ToListAsync(); return result; // return null; } /// /// Retrieves a unique list of enabled feature permissions for a given role. /// This method is optimized to fetch all data in a single, efficient database query. /// /// The ID of the role. /// A distinct list of FeaturePermission objects granted to the role. public async Task> GetFeaturePermissionByRoleID(Guid roleId) { _logger.LogInfo("Fetching feature permissions for RoleID: {RoleId}", roleId); try { // This single, efficient query gets all the required data at once. // It joins the mapping table to the permissions table and filters by the given roleId. var permissions = await ( // 1. Start with the linking table. from rpm in _context.RolePermissionMappings // 2. Join to the FeaturePermissions table on the foreign key. join fp in _context.FeaturePermissions on rpm.FeaturePermissionId equals fp.Id // 3. Apply all filters in one 'where' clause for clarity and efficiency. where rpm.ApplicationRoleId == roleId // Filter by the specific role && fp.IsEnabled == true // And only get enabled permissions // 4. Select the final FeaturePermission object. select fp) .Include(fp => fp.Feature) .Distinct() .ToListAsync(); _logger.LogInfo("Successfully retrieved {PermissionCount} unique permissions for RoleID: {RoleId}", permissions.Count, roleId); return permissions; } catch (Exception ex) { _logger.LogError("An error occurred while fetching permissions for RoleId {RoleId}: {Error}", roleId, ex.Message); // Return an empty list as a safe default to prevent downstream failures. return new List(); } } } }