using Insight.Agent.Interfaces;
using Insight.Agent.Messages;
using Insight.Infrastructure;
using Insight.Infrastructure.Entities;
using MongoDB.Bson;
using MongoDB.Driver;

namespace Insight.Server.Network.Handlers.Agent
{
    public class VirtualMaschineHandler : IAgentMessageHandler<AgentSession>
    {
        private readonly IMongoDatabase _database;

        public VirtualMaschineHandler(IMongoDatabase database)
        {
            _database = database;
        }

        public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage
        {
            if (message is VirtualMaschineList virtualMaschines)
            {
                await OnVirtualMaschinesAsync(sender, virtualMaschines, cancellationToken);
            }
        }

        private async ValueTask OnVirtualMaschinesAsync(AgentSession session, List<VirtualMaschine>? virtualMaschines, CancellationToken cancellationToken)
        {
            var agentEntity = await _database.Agent().Find(Builders<AgentEntity>.Filter.Eq(p => p.Id, session.Id)).FirstOrDefaultAsync(cancellationToken);
            if (agentEntity is null) return;

            var hostEntity = await _database.Host().Find(Builders<HostEntity>.Filter.Eq(p => p.Agent, agentEntity.Id)).FirstOrDefaultAsync(cancellationToken);
            if (hostEntity is null) return;

            var batch = ObjectId.GenerateNewId().ToString();
            var date = DateTime.Now;

            // virtual maschines
            if (virtualMaschines is not null && virtualMaschines.Any())
            {
                var virtualMaschineBulk = new List<WriteModel<HostHypervisorVirtualMaschineEntity>>();

                foreach (var virtualMaschine in virtualMaschines)
                {
                    var virtualMaschineFilter = Builders<HostHypervisorVirtualMaschineEntity>.Filter.And(new List<FilterDefinition<HostHypervisorVirtualMaschineEntity>>
                        {
                            Builders<HostHypervisorVirtualMaschineEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
                            Builders<HostHypervisorVirtualMaschineEntity>.Filter.Eq(x => x.UniqueId, virtualMaschine.Id.ToString())
                        });

                    var virtualMaschineUpdate = Builders<HostHypervisorVirtualMaschineEntity>.Update
                        .SetOnInsert(p => p.Insert, date)
                        .SetOnInsert(p => p.Host, hostEntity.Id)
                        .SetOnInsert(p => p.UniqueId, virtualMaschine.Id.ToString())
                        .Set(p => p.Update, date)
                        .Set(p => p.Batch, batch)

                        .Set(p => p.Name, virtualMaschine?.Name)
                        .Set(p => p.Notes, virtualMaschine?.Notes)
                        .Set(p => p.Enabled, virtualMaschine?.Enabled?.ToString())
                        .Set(p => p.EnabledDefault, virtualMaschine?.EnabledDefault?.ToString())
                        .Set(p => p.Health, virtualMaschine?.HealthState?.ToString())
                        .Set(p => p.Status, virtualMaschine?.Status)
                        .Set(p => p.OnTime, virtualMaschine?.OnTime)
                        .Set(p => p.ReplicationState, virtualMaschine?.ReplicationState?.ToString())
                        .Set(p => p.ReplicationHealth, virtualMaschine?.ReplicationHealth?.ToString())
                        .Set(p => p.ConfigurationVersion, virtualMaschine?.ConfigurationVersion)
                        .Set(p => p.IntegrationServicesVersionState, virtualMaschine?.IntegrationServicesVersionState?.ToString())
                        .Set(p => p.ProcessId, virtualMaschine?.ProcessId)
                        .Set(p => p.NumberOfProcessors, virtualMaschine?.NumberOfProcessors)
                        .Set(p => p.ProcessorLoad, virtualMaschine?.ProcessorLoad)
                        .Set(p => p.MemoryAvailable, virtualMaschine?.MemoryAvailable)
                        .Set(p => p.MemoryUsage, virtualMaschine?.MemoryUsage)
                        .Set(p => p.InstallDate, virtualMaschine?.InstallDate)
                        .Set(p => p.ConfigurationVersion, virtualMaschine?.ConfigurationVersion)
                        .Set(p => p.TimeOfLastStateChange, virtualMaschine?.TimeOfLastStateChange)
                        .Set(p => p.LastReplicationTime, virtualMaschine?.LastReplicationTime)
                        .Set(p => p.Os, virtualMaschine?.GuestOperatingSystem);

                    virtualMaschineBulk.Add(new UpdateOneModel<HostHypervisorVirtualMaschineEntity>(virtualMaschineFilter, virtualMaschineUpdate)
                    {
                        IsUpsert = true
                    });
                }

                virtualMaschineBulk.Add(new DeleteManyModel<HostHypervisorVirtualMaschineEntity>(Builders<HostHypervisorVirtualMaschineEntity>.Filter.And(new List<FilterDefinition<HostHypervisorVirtualMaschineEntity>>
                    {
                        Builders<HostHypervisorVirtualMaschineEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
                        Builders<HostHypervisorVirtualMaschineEntity>.Filter.Ne(x => x.Batch, batch)
                    })));

                var virtualMaschineResult = await _database.HostHypervisorVirtualMaschine().BulkWriteAsync(virtualMaschineBulk, cancellationToken: cancellationToken);
            }

            // virtual maschine configurations

            if (virtualMaschines is not null && virtualMaschines.Any())
            {
                var configurationBulk = new List<WriteModel<HostHypervisorVirtualMaschineConfigEntity>>();

                foreach (var virtualmaschine in virtualMaschines)
                {
                    var virtualMaschineId = await _database.HostHypervisorVirtualMaschine()
                        .Find(p => p.Host == hostEntity.Id && p.UniqueId == virtualmaschine.Id.ToString())
                        .Project(p => p.Id)
                        .FirstOrDefaultAsync();

                    if (virtualmaschine.Configurations is not null && virtualmaschine.Configurations.Any())
                    {
                        foreach (var config in virtualmaschine.Configurations)
                        {
                            var configFilter = Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.And(new List<FilterDefinition<HostHypervisorVirtualMaschineConfigEntity>>
                                {
                                    Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
                                    Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Eq(x => x.VirtualMaschine, virtualMaschineId),
                                    Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Eq(x => x.UniqueId, config.Id)
                                });

                            // custom "notes" concat
                            string notes = string.Empty;
                            if (config?.Notes is not null) foreach (var n in config.Notes) notes += n;

                            var configUpdate = Builders<HostHypervisorVirtualMaschineConfigEntity>.Update
                                .SetOnInsert(p => p.Insert, date)
                                .SetOnInsert(p => p.Host, hostEntity.Id)
                                .SetOnInsert(p => p.VirtualMaschine, virtualMaschineId)
                                .SetOnInsert(p => p.UniqueId, config.Id)
                                .Set(p => p.Update, date)
                                .Set(p => p.Batch, batch)

                                .Set(p => p.ParentId, config.ParentId)
                                .Set(p => p.Type, config.Type)
                                .Set(p => p.Name, config.Name)
                                .Set(p => p.Notes, notes)
                                .Set(p => p.CreationTime, config.CreationTime)
                                .Set(p => p.Generation, config.Generation)
                                .Set(p => p.Architecture, config.Architecture)
                                .Set(p => p.SecureBootEnabled, config.SecureBootEnabled)
                                .Set(p => p.IsAutomaticSnapshot, config.IsAutomaticSnapshot)
                                .Set(p => p.AutomaticStartupAction, config.AutomaticStartupAction?.ToString())
                                .Set(p => p.AutomaticShutdownAction, config.AutomaticShutdownAction?.ToString())
                                .Set(p => p.AutomaticRecoveryAction, config.AutomaticRecoveryAction?.ToString())
                                .Set(p => p.AutomaticSnapshotsEnabled, config.AutomaticSnapshotsEnabled)
                                .Set(p => p.BaseBoardSerialNumber, config.BaseBoardSerialNumber)
                                .Set(p => p.BIOSSerialNumber, config.BIOSSerialNumber)
                                .Set(p => p.BIOSGUID, config.BIOSGUID)
                                .Set(p => p.ConfigurationDataRoot, config.ConfigurationDataRoot)
                                .Set(p => p.ConfigurationFile, config.ConfigurationFile)
                                .Set(p => p.GuestStateDataRoot, config.GuestStateDataRoot)
                                .Set(p => p.GuestStateFile, config.GuestStateFile)
                                .Set(p => p.SnapshotDataRoot, config.SnapshotDataRoot)
                                .Set(p => p.SuspendDataRoot, config.SuspendDataRoot)
                                .Set(p => p.SwapFileDataRoot, config.SwapFileDataRoot);

                            configurationBulk.Add(new UpdateOneModel<HostHypervisorVirtualMaschineConfigEntity>(configFilter, configUpdate)
                            {
                                IsUpsert = true
                            });
                        }
                    }
                }

                configurationBulk.Add(new DeleteManyModel<HostHypervisorVirtualMaschineConfigEntity>(Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.And(new List<FilterDefinition<HostHypervisorVirtualMaschineConfigEntity>>
                            {
                                Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
                                Builders<HostHypervisorVirtualMaschineConfigEntity>.Filter.Ne(x => x.Batch, batch)
                            })));

                var configurationResult = await _database.HostVirtualMaschineConfig().BulkWriteAsync(configurationBulk, cancellationToken: cancellationToken);
            }
        }
    }
}