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 IDbContextFactory _dbContextFactory; private readonly ApplicationDbContext _context; private readonly CacheUpdateHelper _cache; private readonly ILoggingService _logger; public RolesHelper(ApplicationDbContext context, CacheUpdateHelper cache, ILoggingService logger, IDbContextFactory dbContextFactory) { _context = context; _cache = cache; _logger = logger; _dbContextFactory = dbContextFactory; } /// /// 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 using the main thread's context --- // This is safe because the query is not executed yet. var employeeRoleIdsQuery = _context.EmployeeRoleMappings .Where(erm => erm.EmployeeId == EmployeeId && erm.IsEnabled) .Select(erm => erm.RoleId); // --- Step 2: Asynchronously update the cache using the DbContextFactory --- _ = Task.Run(async () => { try { // Create a NEW, short-lived DbContext instance for this background task. await using var contextForCache = await _dbContextFactory.CreateDbContextAsync(); // Now, re-create and execute the query using this new, isolated context. var roleIds = await contextForCache.EmployeeRoleMappings .Where(erm => erm.EmployeeId == EmployeeId && erm.IsEnabled) .Select(erm => erm.RoleId) .ToListAsync(); if (roleIds.Any()) { // The cache service might also need its own context, or you can pass the data directly. // Assuming AddApplicationRole takes the data, not a context. await _cache.AddApplicationRole(EmployeeId, roleIds); _logger.LogInfo("Successfully queued cache update for EmployeeId: {EmployeeId}", EmployeeId); } } catch (Exception ex) { _logger.LogWarning("Background cache update failed for EmployeeId {EmployeeId} : {Error}", EmployeeId, ex.Message); } }); // --- Step 3: Execute the main query on the main thread using its original context --- // This is now safe because the background task is using a different DbContext instance. var roleIds = await employeeRoleIdsQuery.ToListAsync(); var permissionIds = await _context.RolePermissionMappings .Where(rp => roleIds.Contains(rp.ApplicationRoleId)).Select(rp => rp.FeaturePermissionId).ToListAsync(); var permissions = await _context.FeaturePermissions.Include(f => f.Feature) .Where(fp => permissionIds.Contains(fp.Id)) .Distinct() .ToListAsync(); _logger.LogInfo("Successfully retrieved {PermissionCount} unique permissions for EmployeeId: {EmployeeId}", permissions.Count, EmployeeId); return permissions; } catch (Exception ex) { _logger.LogError(ex, "An error occurred while fetching permissions for EmployeeId {EmployeeId}", EmployeeId); 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(ex, "An error occurred while fetching permissions for RoleId {RoleId}", roleId); // Return an empty list as a safe default to prevent downstream failures. return new List(); } } } }