

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<ApplicationDbContext> _dbContextFactory;
        private readonly ApplicationDbContext _context;
        private readonly CacheUpdateHelper _cache;
        private readonly ILoggingService _logger;
        public RolesHelper(ApplicationDbContext context, CacheUpdateHelper cache, ILoggingService logger, IDbContextFactory<ApplicationDbContext> dbContextFactory)
        {
            _context = context;
            _cache = cache;
            _logger = logger;
            _dbContextFactory = dbContextFactory;
        }

        /// <summary>
        /// Retrieves a unique list of enabled feature permissions for a given employee.
        /// This method is optimized to use a single, composed database query.
        /// </summary>
        /// <param name="EmployeeId">The ID of the employee.</param>
        /// <returns>A distinct list of FeaturePermission objects the employee is granted.</returns>
        public async Task<List<FeaturePermission>> GetFeaturePermissionByEmployeeId(Guid EmployeeId, Guid tenantId)
        {
            _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
                    .AsNoTracking()
                    .Where(erm => erm.EmployeeId == EmployeeId && erm.IsEnabled && erm.TenantId == tenantId)
                    .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
                            .AsNoTracking()
                            .Where(erm => erm.EmployeeId == EmployeeId && erm.IsEnabled && erm.TenantId == tenantId)
                            .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, tenantId);
                            _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
                    .AsNoTracking()
                    .Where(rp => roleIds.Contains(rp.ApplicationRoleId)).Select(rp => rp.FeaturePermissionId).ToListAsync();

                var permissions = await _context.FeaturePermissions
                    .AsNoTracking()
                    .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<FeaturePermission>();
            }
        }

        public async Task<List<FeaturePermission>> GetFeaturePermissionByRoleID1(Guid roleId)
        {
            List<Guid> 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;
        }

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="roleId">The ID of the role.</param>
        /// <returns>A distinct list of FeaturePermission objects granted to the role.</returns>
        public async Task<List<FeaturePermission>> 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<FeaturePermission>();
            }
        }

    }
}
