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 ProcessorHandler : IAgentMessageHandler<AgentSession>
    {
        private readonly IMongoDatabase _database;

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

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

        private async ValueTask OnProcessorsAsync(AgentSession session, List<Processor> processors, 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;

            var bulk = new List<WriteModel<HostProcessorEntity>>();

            if (processors is not null && processors.Any())
            {
                foreach (var processor in processors)
                {
                    var filterDefinition = Builders<HostProcessorEntity>.Filter.And(new List<FilterDefinition<HostProcessorEntity>>
                        {
                            Builders<HostProcessorEntity>.Filter.Eq(x => x.Host, hostEntity.Id),
                            Builders<HostProcessorEntity>.Filter.Eq(x => x.Index, processor.Index)
                        });

                    var updateDefinition = Builders<HostProcessorEntity>.Update
                        .SetOnInsert(p => p.Insert, date)
                        .SetOnInsert(p => p.Host, hostEntity.Id)
                        .SetOnInsert(p => p.Index, processor.Index)
                        .Set(p => p.Update, date)
                        .Set(p => p.Batch, batch)
                        .Set(p => p.Company, processor.Manufacturer)
                        .Set(p => p.Name, processor.Name)
                        .Set(p => p.Socket, processor.Socket)
                        .Set(p => p.Serial, processor.SerialNumber)
                        .Set(p => p.Version, processor.Version)
                        .Set(p => p.Cores, processor.Cores)
                        .Set(p => p.LogicalCores, processor.LogicalCores)
                        .Set(p => p.Clock, processor.MaxSpeed)
                        .Set(p => p.CurrentClock, processor.CurrentSpeed)
                        .Set(p => p.L1Size, processor.L1Size)
                        .Set(p => p.L2Size, processor.L2Size)
                        .Set(p => p.L3Size, processor.L3Size)
                        .Set(p => p.Virtualization, processor.Virtualization)
                        .Set(p => p.PNP, processor.DeviceId);

                    bulk.Add(new UpdateOneModel<HostProcessorEntity>(filterDefinition, updateDefinition)
                    {
                        IsUpsert = true
                    });
                }
            }

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

            var result = await _database.HostProcessor().BulkWriteAsync(bulk, cancellationToken: cancellationToken);
        }
    }
}