using Marco.Pms.Model.MongoDBModels.Employees;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;

namespace Marco.Pms.Helpers.CacheHelper
{
    public class EmployeeCache
    {
        private readonly IMongoCollection<EmployeePermissionMongoDB> _collection;
        private readonly ILogger<EmployeeCache> _logger;

        public EmployeeCache(IConfiguration configuration, ILogger<EmployeeCache> logger)
        {
            _logger = logger;
            var connectionString = configuration["MongoDB:ConnectionString"];
            var mongoUrl = new MongoUrl(connectionString);
            var client = new MongoClient(mongoUrl); // Your MongoDB connection string
            var mongoDB = client.GetDatabase(mongoUrl.DatabaseName);  // Your MongoDB Database name
            _collection = mongoDB.GetCollection<EmployeePermissionMongoDB>("EmployeeProfile");
        }
        public async Task<bool> AddApplicationRoleToCache(Guid employeeId, List<string> newRoleIds, List<string> newPermissionIds, Guid tenantId)
        {

            // 2. Perform database queries concurrently for better performance.
            var employeeIdString = employeeId.ToString();
            var tenantIdString = tenantId.ToString();

            // 5. Build a single, efficient update operation.
            var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeIdString);
            filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

            var update = Builders<EmployeePermissionMongoDB>.Update
                .AddToSetEach(e => e.ApplicationRoleIds, newRoleIds)
                .Set(r => r.ExpireAt, DateTime.UtcNow.Date.AddDays(1))
                .Set(r => r.TenantId, tenantIdString)
                .AddToSetEach(e => e.PermissionIds, newPermissionIds);

            var options = new UpdateOptions { IsUpsert = true };

            var result = await _collection.UpdateOneAsync(filter, update, options);

            await InitializeCollectionAsync();

            // 6. Return a more accurate result indicating success for both updates and upserts.
            // The operation is successful if an existing document was modified OR a new one was created.
            return result.IsAcknowledged && (result.ModifiedCount > 0 || result.UpsertedId != null);
        }
        public async Task<bool> AddProjectsToCache(Guid employeeId, List<Guid> projectIds, Guid tenantId)
        {
            var newprojectIds = projectIds.Select(p => p.ToString()).ToList();
            var tenantIdString = tenantId.ToString();

            var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
            filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

            var update = Builders<EmployeePermissionMongoDB>.Update
                .Set(r => r.ExpireAt, DateTime.UtcNow.Date.AddDays(1))
                .Set(r => r.TenantId, tenantIdString)
                .AddToSetEach(e => e.ProjectIds, newprojectIds);

            var result = await _collection.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true });
            if (result.MatchedCount == 0)
            {
                return false;
            }
            await InitializeCollectionAsync();
            return true;
        }
        public async Task<List<Guid>> GetProjectsFromCache(Guid employeeId, Guid tenantId)
        {
            var tenantIdString = tenantId.ToString();

            var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
            filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

            var result = await _collection
                .Find(filter)
                .FirstOrDefaultAsync();

            var projectIds = new List<Guid>();
            if (result != null)
            {
                projectIds = result.ProjectIds.Select(Guid.Parse).ToList();
            }

            return projectIds;
        }
        public async Task<List<Guid>> GetPermissionsFromCache(Guid employeeId, Guid tenantId)
        {
            var tenantIdString = tenantId.ToString();

            var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
            filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

            var result = await _collection
                .Find(filter)
                .FirstOrDefaultAsync();

            var permissionIds = new List<Guid>();
            if (result != null)
            {
                permissionIds = result.PermissionIds.Select(Guid.Parse).ToList();
            }

            return permissionIds;
        }
        public async Task<bool> ClearAllProjectIdsFromCache(Guid employeeId, Guid tenantId)
        {
            var tenantIdString = tenantId.ToString();

            var filter = Builders<EmployeePermissionMongoDB>.Filter
                .Eq(e => e.Id, employeeId.ToString());
            filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

            var update = Builders<EmployeePermissionMongoDB>.Update
                .Set(e => e.ProjectIds, new List<string>());

            var result = await _collection.UpdateOneAsync(filter, update);

            if (result.ModifiedCount == 0)
                return false;

            return true;
        }
        public async Task<bool> ClearAllProjectIdsByRoleIdFromCache(Guid roleId)
        {
            var filter = Builders<EmployeePermissionMongoDB>.Filter.AnyEq(e => e.ApplicationRoleIds, roleId.ToString());

            var update = Builders<EmployeePermissionMongoDB>.Update
                .Set(e => e.ProjectIds, new List<string>());

            var result = await _collection.UpdateOneAsync(filter, update);

            if (result.MatchedCount == 0)
                return false;

            return true;
        }
        public async Task<bool> ClearAllProjectIdsByPermissionIdFromCache(Guid permissionId, Guid tenantId)
        {
            var tenantIdString = tenantId.ToString();

            var filter = Builders<EmployeePermissionMongoDB>.Filter.AnyEq(e => e.PermissionIds, permissionId.ToString());
            filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

            var update = Builders<EmployeePermissionMongoDB>.Update.Set(e => e.ProjectIds, new List<string>());

            var result = await _collection.UpdateManyAsync(filter, update).ConfigureAwait(false);
            return result.IsAcknowledged && result.ModifiedCount > 0;
        }
        public async Task<bool> RemoveRoleIdFromCache(Guid employeeId, Guid roleId, Guid tenantId)
        {
            var tenantIdString = tenantId.ToString();

            var filter = Builders<EmployeePermissionMongoDB>.Filter
                .Eq(e => e.Id, employeeId.ToString());
            filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

            var update = Builders<EmployeePermissionMongoDB>.Update
                .Pull(e => e.ApplicationRoleIds, roleId.ToString());

            var result = await _collection.UpdateOneAsync(filter, update);

            if (result.MatchedCount == 0)
                return false;

            if (result.ModifiedCount == 0)
                return false;

            return true;
        }
        public async Task<bool> ClearAllPermissionIdsByEmployeeIDFromCache(Guid employeeId, Guid tenantId)
        {
            var tenantIdString = tenantId.ToString();

            var filter = Builders<EmployeePermissionMongoDB>.Filter
                .Eq(e => e.Id, employeeId.ToString());
            filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

            var update = Builders<EmployeePermissionMongoDB>.Update
                .Set(e => e.PermissionIds, new List<string>());

            var result = await _collection.UpdateOneAsync(filter, update);

            if (result.MatchedCount == 0)
                return false;

            return true;
        }
        public async Task<bool> ClearAllPermissionIdsByRoleIdFromCache(Guid roleId)
        {
            var filter = Builders<EmployeePermissionMongoDB>.Filter.AnyEq(e => e.ApplicationRoleIds, roleId.ToString());

            var update = Builders<EmployeePermissionMongoDB>.Update
                .Set(e => e.PermissionIds, new List<string>());

            var result = await _collection.UpdateOneAsync(filter, update);

            if (result.MatchedCount == 0)
                return false;

            return true;
        }
        public async Task<bool> ClearAllEmployeesFromCache()
        {
            var result = await _collection.DeleteManyAsync(FilterDefinition<EmployeePermissionMongoDB>.Empty);

            if (result.DeletedCount == 0)
                return false;

            return true;
        }
        public async Task<bool> ClearAllEmployeesFromCacheByOnlyEmployeeId(Guid employeeId)
        {
            var employeeIdString = employeeId.ToString();

            try
            {
                var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeIdString);

                var result = await _collection.DeleteManyAsync(filter);

                if (result.DeletedCount == 0)
                    return false;

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occured while deleting employee profile");
                return false;
            }
        }
        public async Task<bool> ClearAllEmployeesFromCacheByTenantId(Guid tenantId)
        {
            var tenantIdString = tenantId.ToString();

            try
            {
                var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

                var result = await _collection.DeleteManyAsync(filter);

                if (result.DeletedCount == 0)
                    return false;

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occured while deleting employee profile");
                return false;
            }
        }
        public async Task<bool> ClearAllEmployeesFromCacheByEmployeeIds(List<string> employeeIds, Guid tenantId)
        {
            var tenantIdString = tenantId.ToString();

            try
            {
                var filter = Builders<EmployeePermissionMongoDB>.Filter.In(x => x.Id, employeeIds);
                filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);

                var result = await _collection.DeleteManyAsync(filter);

                if (result.DeletedCount == 0)
                    return false;

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occured while deleting employee profile");
                return false;
            }
        }

        // A private method to handle the one-time setup of the collection's indexes.
        private async Task InitializeCollectionAsync()
        {
            var indexKeys = Builders<EmployeePermissionMongoDB>.IndexKeys.Ascending(x => x.ExpireAt);
            var indexOptions = new CreateIndexOptions
            {
                ExpireAfter = TimeSpan.Zero // required for fixed expiration time
            };
            var indexModel = new CreateIndexModel<EmployeePermissionMongoDB>(indexKeys, indexOptions);
            await _collection.Indexes.CreateOneAsync(indexModel);
        }
    }
}
