using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Services.Helpers;
using MarcoBMS.Services.Helpers;
using Microsoft.EntityFrameworkCore;

namespace Marco.Pms.Services.Service
{
    public class PermissionServices
    {
        private readonly ApplicationDbContext _context;
        private readonly RolesHelper _rolesHelper;
        private readonly CacheUpdateHelper _cache;
        private readonly Guid tenantId;

        public PermissionServices(ApplicationDbContext context, RolesHelper rolesHelper, CacheUpdateHelper cache, UserHelper userHelper)
        {
            _context = context;
            _rolesHelper = rolesHelper;
            _cache = cache;
            tenantId = userHelper.GetTenantId();
        }

        //public async Task<bool> HasPermission(Guid featurePermissionId, Guid employeeId, Guid? projectId = null)
        //{
        //    var featurePermissionIds = await _cache.GetPermissions(employeeId);
        //    if (featurePermissionIds == null)
        //    {
        //        List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId);
        //        featurePermissionIds = featurePermission.Select(fp => fp.Id).ToList();
        //    }
        //    if (projectId != null)
        //    {
        //        var projectLevelPerissionIds = await _context.ProjectLevelPermissionMappings
        //            .Where(pl => pl.ProjectId == projectId.Value && pl.EmployeeId == employeeId).Select(pl => pl.PermissionId).ToListAsync();

        //        var projectLevelModuleIds = new HashSet<Guid>
        //        {
        //            Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def"),
        //            Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"),
        //            Guid.Parse("81ab8a87-8ccd-4015-a917-0627cee6a100"),
        //            Guid.Parse("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"),
        //            Guid.Parse("a8cf4331-8f04-4961-8360-a3f7c3cc7462")
        //        };

        //        var allProjectLevelPermissionIds = await _context.FeaturePermissions
        //            .Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) && !projectLevelPerissionIds.Contains(fp.Id)).Select(fp => fp.Id).ToListAsync();
        //        featurePermissionIds.RemoveRange(allProjectLevelPermissionIds);

        //        featurePermissionIds.AddRange(projectLevelPerissionIds);
        //        featurePermissionIds = featurePermissionIds.Distinct().ToList();
        //    }
        //    var hasPermission = featurePermissionIds.Contains(featurePermissionId);
        //    return hasPermission;
        //}

        /// <summary>
        /// Checks whether an employee has a specific feature permission, optionally within a project context.
        /// </summary>
        /// <param name="featurePermissionId">The target feature permission ID to check.</param>
        /// <param name="employeeId">The ID of the employee.</param>
        /// <param name="projectId">Optional project ID for project-scoped permissions.</param>
        /// <returns>True if the user has the permission, otherwise false.</returns>
        public async Task<bool> HasPermission(Guid featurePermissionId, Guid employeeId, Guid? projectId = null)
        {
            // 1. Try fetching permissions from cache (fast-path lookup).
            var featurePermissionIds = await _cache.GetPermissions(employeeId, tenantId);

            // If not found in cache, fallback to database (slower).
            if (featurePermissionIds == null)
            {
                var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId, tenantId);
                featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList();
            }

            // 2. Handle project-level permission overrides if a project is specified.
            if (projectId.HasValue)
            {
                // Fetch permissions explicitly assigned to this employee in the project.
                var projectLevelPermissionIds = await _context.ProjectLevelPermissionMappings
                    .AsNoTracking()
                    .Where(pl => pl.ProjectId == projectId.Value && pl.EmployeeId == employeeId)
                    .Select(pl => pl.PermissionId)
                    .ToListAsync();

                if (projectLevelPermissionIds?.Any() ?? false)
                {

                    // Define modules where project-level overrides apply.
                    var projectLevelModuleIds = new HashSet<Guid>
                    {
                        Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def"),
                        Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"),
                        Guid.Parse("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"),
                        Guid.Parse("a8cf4331-8f04-4961-8360-a3f7c3cc7462")
                    };

                    // Get all feature permissions under those modules where the user didn't have explicit project-level grants.
                    var allOverriddenPermissions = await _context.FeaturePermissions
                        .AsNoTracking()
                        .Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) &&
                                     !projectLevelPermissionIds.Contains(fp.Id))
                        .Select(fp => fp.Id)
                        .ToListAsync();

                    // Apply overrides:
                    // - Remove global permissions overridden by project-level rules.
                    // - Add explicit project-level permissions.
                    featurePermissionIds = featurePermissionIds
                        .Except(allOverriddenPermissions)       // Remove overridden
                        .Concat(projectLevelPermissionIds)      // Add project-level
                        .Distinct()                             // Ensure no duplicates
                        .ToList();
                }
            }

            // 3. Final check: does the employee have the requested permission?
            return featurePermissionIds.Contains(featurePermissionId);
        }
        public async Task<bool> HasPermissionAny(List<Guid> featurePermissionIds, Guid employeeId)
        {
            var allFeaturePermissionIds = await _cache.GetPermissions(employeeId, tenantId);
            if (allFeaturePermissionIds == null)
            {
                List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId, tenantId);
                allFeaturePermissionIds = featurePermission.Select(fp => fp.Id).ToList();
            }
            var hasPermission = featurePermissionIds.Any(f => allFeaturePermissionIds.Contains(f));

            return hasPermission;
        }
        public async Task<bool> HasProjectPermission(Employee LoggedInEmployee, Guid projectId)
        {
            var employeeId = LoggedInEmployee.Id;
            var projectIds = await _cache.GetProjects(employeeId, tenantId);

            if (projectIds == null)
            {
                var hasPermission = await HasPermission(PermissionsMaster.ManageProject, employeeId);
                if (hasPermission)
                {
                    var projects = await _context.Projects.AsNoTracking().Where(c => c.TenantId == LoggedInEmployee.TenantId).ToListAsync();
                    projectIds = projects.Select(p => p.Id).ToList();
                }
                else
                {
                    var allocation = await _context.ProjectAllocations.AsNoTracking().Where(c => c.EmployeeId == employeeId && c.IsActive).ToListAsync();
                    if (!allocation.Any())
                    {
                        return false;
                    }
                    projectIds = allocation.Select(c => c.ProjectId).Distinct().ToList();
                }
                await _cache.AddProjects(LoggedInEmployee.Id, projectIds, tenantId);
            }
            return projectIds.Contains(projectId);
        }
    }
}
