using Marco.Pms.Model.MongoDBModels;
using Marco.Pms.Model.MongoDBModels.Utility;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using System.Collections;

namespace Marco.Pms.Helpers.Utility
{
    public class UtilityMongoDBHelper
    {
        private readonly IMongoDatabase _mongoDatabase;
        private readonly IConfiguration _configuration;
        private readonly ILogger<UtilityMongoDBHelper> _logger;
        public UtilityMongoDBHelper(IConfiguration configuration, ILogger<UtilityMongoDBHelper> logger)
        {
            _configuration = configuration;
            _logger = logger;
            var connectionString = configuration["MongoDB:ModificationConnectionString"];
            var mongoUrl = new MongoUrl(connectionString);
            var client = new MongoClient(mongoUrl); // Your MongoDB connection string
            _mongoDatabase = client.GetDatabase(mongoUrl.DatabaseName);  // Your MongoDB Database name
        }

        #region =================================================================== Update Log Helper Functions ===================================================================
        public async Task PushToUpdateLogsAsync(UpdateLogsObject oldObject, string collectionName)
        {
            try
            {
                var collection = _mongoDatabase.GetCollection<UpdateLogsObject>(collectionName);
                await collection.InsertOneAsync(oldObject);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while saving object of update logs in collection: {Collection}", collectionName);
            }
        }
        public async Task PushListToUpdateLogsAsync(List<UpdateLogsObject> oldObjects, string collectionName)
        {
            try
            {
                var collection = _mongoDatabase.GetCollection<UpdateLogsObject>(collectionName);
                if (oldObjects.Any())
                {
                    await collection.InsertManyAsync(oldObjects);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while saving list of update logs in collection: {Collection}", collectionName);
            }
        }

        public async Task<List<UpdateLogsObject>> GetFromUpdateLogsByEntityIdAsync(Guid entityId, string collectionName)
        {
            var collection = _mongoDatabase.GetCollection<UpdateLogsObject>(collectionName);
            var filter = Builders<UpdateLogsObject>.Filter.Eq(p => p.EntityId, entityId.ToString());

            List<UpdateLogsObject> result = await collection
              .Find(filter)
              .ToListAsync();

            return result;
        }

        public async Task<List<UpdateLogsObject>> GetFromUpdateLogsByUpdetedByIdAsync(Guid updatedById, string collectionName)
        {
            var collection = _mongoDatabase.GetCollection<UpdateLogsObject>(collectionName);
            var filter = Builders<UpdateLogsObject>.Filter.Eq(p => p.UpdatedById, updatedById.ToString());

            List<UpdateLogsObject> result = await collection
              .Find(filter)
              .ToListAsync();

            return result;
        }

        public BsonDocument EntityToBsonDocument(object entity)
        {
            var bson = new BsonDocument();

            var props = entity.GetType().GetProperties();
            foreach (var prop in props)
            {
                var value = prop.GetValue(entity);
                if (value == null)
                {
                    bson[prop.Name] = BsonNull.Value;
                    continue;
                }

                if (value is Guid guidValue)
                {
                    bson[prop.Name] = guidValue.ToString(); // store Guid as string
                }
                else if (value is string || value.GetType().IsPrimitive || value is DateTime)
                {
                    bson[prop.Name] = BsonValue.Create(value); // simple types
                }
                else if (value is IEnumerable list && !(value is string))
                {
                    var array = new BsonArray();
                    foreach (var item in list)
                    {
                        array.Add(EntityToBsonDocument(item)); // recursive
                    }
                    bson[prop.Name] = array;
                }
                else
                {
                    // nested object
                    continue;
                }
            }

            return bson;
        }

        #endregion

        #region =================================================================== S3 deletion Helper Functions ===================================================================

        public async Task PushToS3DeletionAsync(List<S3DeletionObject> deletionObject)
        {
            var bucketName = _configuration["AWS:BucketName"];
            if (bucketName != null)
            {
                deletionObject = deletionObject.Select(d => new S3DeletionObject
                {
                    BucketName = bucketName,
                    Key = d.Key,
                    Deleted = false
                }).ToList();
            }
            _logger.LogInformation("Delection object for bucket {BucketName} added to mongoDB", bucketName);
            try
            {
                var collection = _mongoDatabase.GetCollection<S3DeletionObject>("S3Delection");
                await collection.InsertManyAsync(deletionObject);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occured while saving delection object for S3 to MogoDB");
            }
            _logger.LogInformation("Delection Objects added to MongoDB Successfully");
        }

        #endregion

        #region =================================================================== NotificatioBody Helper Functions ===================================================================

        public async Task AddNotificationAsync(NotificationMongoDB notification)
        {
            try
            {
                var collection = _mongoDatabase.GetCollection<NotificationMongoDB>("NotificatioBody");

                var indexKeys = Builders<NotificationMongoDB>.IndexKeys
                    .Ascending(doc => doc.TenantId)
                    .Ascending(doc => doc.Name);

                // Define index options with unique constraint
                var indexOptions = new CreateIndexOptions { Unique = true };

                // Create the index model
                var indexModel = new CreateIndexModel<NotificationMongoDB>(indexKeys, indexOptions);

                // Create the index on the collection (this operation is idempotent)
                collection.Indexes.CreateOne(indexModel);

                await collection.InsertOneAsync(notification);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while adding the Notification body {NotificationName} for tenant {TenantId}", notification.Name, notification.TenantId);
            }
        }

        public async Task<NotificationMongoDB> GetNotificationBodyAsync(string name, Guid tenantId)
        {
            var rootTenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26");
            NotificationMongoDB? result = null;
            NotificationMongoDB? defaultNotification = new NotificationMongoDB
            {
                Name = "default",
                Title = "Error: Something Went Wrong",
                Body = " An unexpected error occurred. Please try again. If the problem persists, contact support",
                Parameters = "",
                TenantId = rootTenantId
            };

            var collection = _mongoDatabase.GetCollection<NotificationMongoDB>("NotificatioBody");
            try
            {
                var filter = Builders<NotificationMongoDB>.Filter.And(
                        Builders<NotificationMongoDB>.Filter.Eq(n => n.Name, name),
                        Builders<NotificationMongoDB>.Filter.Eq(n => n.TenantId, tenantId)

                    );

                result = await collection
                  .Find(filter)
                  .FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while fetching the Notification body {NotificationName} for tenant {TenantId}", name, tenantId);
            }
            if (result == null)
            {
                try
                {
                    var filter = Builders<NotificationMongoDB>.Filter.And(
                         Builders<NotificationMongoDB>.Filter.Eq(n => n.Name, name),
                         Builders<NotificationMongoDB>.Filter.Eq(n => n.TenantId, rootTenantId)

                     );
                    result = await collection.Find(filter).FirstOrDefaultAsync();
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Exception occured while fetching the Notification body {NotificationName} for tenant {TenantId}", name, rootTenantId);
                    return defaultNotification;
                }
            }
            return result;
        }

        #endregion
    }
}
