using Insight.Agent.Interfaces;
using Insight.Agent.Messages;
using System.Management;
using System.Runtime.Versioning;
using static Insight.Agent.Messages.VirtualMaschine;
using static Insight.Agent.Messages.VirtualMaschineConfiguration;

namespace Insight.Agent.Network.Handlers
{
    [SupportedOSPlatform("windows")]
    public class VirtualMaschineHandler : IAgentMessageHandler<AgentSession>
    {
        public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IAgentMessage
        {
            if (message is GetInventory)
            {
                var result = new VirtualMaschineList();
                result.AddRange(GetVirtualMaschines());

                await sender.SendAsync(result, cancellationToken);
            }
        }

        private static List<VirtualMaschine> GetVirtualMaschines()
        {
            using var searcher = new ManagementObjectSearcher
            {
                Scope = new ManagementScope(@"root\virtualization\v2"),
                Query = new ObjectQuery("select * msvm_computersystem")
            };

            if (searcher.TryGet(out var computersystems) is false)
            {
                searcher.Query = new ObjectQuery("select * from msvm_computersystem");

                if (searcher.TryGet(out computersystems) is false) throw new InvalidOperationException("WMI Collection NULL");
            }

            var vms = new List<VirtualMaschine>();

            using (computersystems)
            {
                foreach (ManagementObject cs in computersystems.Cast<ManagementObject>())
                {
                    var vm = new VirtualMaschine();

                    var csProperties = cs.GetPropertyHashes();

                    var vmId = cs.GetValue<string>(csProperties, "name")?.Trim();

                    if (Guid.TryParse(vmId, out var vmGuid) is false) continue;

                    vm.Id = vmGuid;
                    vm.ProcessId = cs.GetValue<uint>(csProperties, "processid");
                    vm.Caption = cs.GetValue<string>(csProperties, "caption")?.Trim();
                    vm.Name = cs.GetValue<string>(csProperties, "elementname")?.Trim();
                    vm.Enabled = (EnabledEnum)cs.GetValue<ushort>(csProperties, "enabledstate");
                    vm.EnabledDefault = (EnabledDefaultEnum)cs.GetValue<ushort>(csProperties, "enableddefault");
                    vm.HealthState = (HealthStatusEnum)cs.GetValue<ushort>(csProperties, "healthstate");
                    vm.Status = cs.GetValue<string>(csProperties, "status")?.Trim();
                    vm.OnTime = cs.GetValue<ulong>(csProperties, "ontimeinmilliseconds");
                    vm.ReplicationMode = cs.GetValue<ushort>(csProperties, "replicationmode");
                    vm.ReplicationState = (ReplicationStateEnum)cs.GetValue<ushort>(csProperties, "replicationstate");
                    vm.ReplicationHealth = (ReplicationHealthEnum)cs.GetValue<ushort>(csProperties, "replicationhealth");

                    if (cs.TryGetValue<object>(csProperties, "installdate", out var installdate))
                    {
                        vm.InstallDate = ManagementDateTimeConverter.ToDateTime(installdate?.ToString());
                    }

                    if (cs.TryGetValue<object>(csProperties, "timeoflastconfigurationchange", out var timeoflastconfigurationchange))
                    {
                        vm.TimeOfLastConfigurationChange = ManagementDateTimeConverter.ToDateTime(timeoflastconfigurationchange?.ToString());
                    }

                    if (cs.TryGetValue<object>(csProperties, "timeoflaststatechange", out var timeoflaststatechange))
                    {
                        vm.TimeOfLastStateChange = ManagementDateTimeConverter.ToDateTime(timeoflaststatechange?.ToString());
                    }

                    if (cs.TryGetValue<object>(csProperties, "lastreplicationtime", out var lastreplicationtime))
                    {
                        vm.LastReplicationTime = ManagementDateTimeConverter.ToDateTime(lastreplicationtime?.ToString());
                    }

                    var summaryinformation = cs.GetRelated("msvm_summaryinformation");
                    using (summaryinformation)
                    {
                        foreach (ManagementObject si in summaryinformation.Cast<ManagementObject>())
                        {
                            var siProperties = si.GetPropertyHashes();

                            vm.Notes = si.GetValue<string>(siProperties, "Notes");
                            vm.ConfigurationVersion = si.GetValue<string>(siProperties, "Version");
                            vm.IntegrationServicesVersionState = (IntegrationServicesVersionStateEnum)si.GetValue<ushort>(siProperties, "IntegrationServicesVersionState");
                            vm.GuestOperatingSystem = si.GetValue<string>(siProperties, "GuestOperatingSystem");
                            vm.NumberOfProcessors = si.GetValue<ushort>(siProperties, "NumberOfProcessors");
                            vm.ProcessorLoad = si.GetValue<ushort>(siProperties, "ProcessorLoad");
                            vm.MemoryAvailable = si.GetValue<int>(siProperties, "MemoryAvailable");
                            vm.MemoryUsage = si.GetValue<ulong>(siProperties, "MemoryUsage");
                        }
                    }

                    var virtualSystemSettingData = cs.GetRelated("Msvm_VirtualSystemSettingData");
                    using (virtualSystemSettingData)
                    {
                        var configs = new List<VirtualMaschineConfiguration>();

                        foreach (ManagementObject vssd in virtualSystemSettingData.Cast<ManagementObject>())
                        {
                            var vmc = new VirtualMaschineConfiguration();

                            var vssdProperties = vssd.GetPropertyHashes();

                            var vmcId = vssd.GetValue<string>(vssdProperties, "ConfigurationID")?.Trim();

                            if (Guid.TryParse(vmcId, out var vmcGuid) is false) continue;

                            vmc.Id = vmcGuid.ToString();

                            vmc.Type = vssd.GetValue<string>(vssdProperties, "VirtualSystemType");
                            vmc.Name = vssd.GetValue<string>(vssdProperties, "ElementName");

                            if (vssd.TryGetValue<object>(vssdProperties, "CreationTime", out var creationtime))
                            {
                                vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(creationtime?.ToString());
                            }

                            vmc.Generation = vssd.GetValue<string>(vssdProperties, "VirtualSystemSubType");
                            vmc.Architecture = vssd.GetValue<string>(vssdProperties, "Architecture");
                            vmc.AutomaticStartupAction = (AutomaticStartupActionEnum)vssd.GetValue<uint>(vssdProperties, "AutomaticStartupAction");
                            vmc.AutomaticShutdownAction = (AutomaticShutdownActionEnum)vssd.GetValue<uint>(vssdProperties, "AutomaticShutdownAction");
                            vmc.AutomaticRecoveryAction = (AutomaticRecoveryActionEnum)vssd.GetValue<uint>(vssdProperties, "AutomaticRecoveryAction");
                            vmc.AutomaticSnapshotsEnabled = vssd.GetValue<bool>(vssdProperties, "AutomaticSnapshotsEnabled");

                            //if (vssd.TryGetValue<object>(vssdProperties, "AutomaticStartupActionDelay", out var automaticstartupactiondelay))
                            //{
                            //    vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(automaticstartupactiondelay?.ToString());
                            //}

                            vmc.BaseBoardSerialNumber = vssd.GetValue<string>(vssdProperties, "BaseBoardSerialNumber");
                            vmc.BIOSGUID = vssd.GetValue<string>(vssdProperties, "BIOSGUID");
                            vmc.BIOSSerialNumber = vssd.GetValue<string>(vssdProperties, "BIOSSerialNumber");
                            vmc.BootOrder = vssd.GetValue<ushort[]>(vssdProperties, "BootOrder");
                            vmc.ConfigurationDataRoot = vssd.GetValue<string>(vssdProperties, "ConfigurationDataRoot");
                            vmc.ConfigurationFile = vssd.GetValue<string>(vssdProperties, "ConfigurationFile");
                            vmc.GuestStateDataRoot = vssd.GetValue<string>(vssdProperties, "GuestStateDataRoot");
                            vmc.GuestStateFile = vssd.GetValue<string>(vssdProperties, "GuestStateFile");
                            vmc.SnapshotDataRoot = vssd.GetValue<string>(vssdProperties, "SnapshotDataRoot");
                            vmc.SuspendDataRoot = vssd.GetValue<string>(vssdProperties, "SuspendDataRoot");
                            vmc.SwapFileDataRoot = vssd.GetValue<string>(vssdProperties, "SwapFileDataRoot");
                            vmc.SecureBootEnabled = vssd.GetValue<bool>(vssdProperties, "SecureBootEnabled");
                            vmc.IsAutomaticSnapshot = vssd.GetValue<bool>(vssdProperties, "IsAutomaticSnapshot");
                            vmc.Notes = vssd.GetValue<string[]>(vssdProperties, "Notes");

                            if (vssd.GetValue<string>(vssdProperties, "Parent") is string parent)
                            {
                                using var vmcp = new ManagementObject(parent);
                                vmcp.Get();

                                if (Guid.TryParse(vmcp["ConfigurationID"]?.ToString(), out var parentGuid) is false) continue;
                                vmc.ParentId = parentGuid.ToString();
                            }

                            //var storageallocationsettingdata = cs.GetRelated("Msvm_StorageAllocationSettingData");
                            //using (storageallocationsettingdata)
                            //{

                            //}

                            configs.Add(vmc);
                        }

                        vm.Configurations = configs.GroupBy(p => p.Id).Select(p => p.First()).ToList();
                    }

                    vms.Add(vm);
                }
            }

            return vms;
        }

        private static List<VirtualMaschine> QueryVirtualMaschines0()
        {
            using var searcher = new ManagementObjectSearcher
            {
                Scope = new ManagementScope(@"root\virtualization\v2"),
                Query = new ObjectQuery("select * msvm_computersystem")
            };

            if (searcher.TryGet(out var computersystems) is false)
            {
                searcher.Query = new ObjectQuery("select * from msvm_computersystem");

                if (searcher.TryGet(out computersystems) is false) throw new InvalidOperationException("WMI Collection NULL");
            }

            var vms = new List<VirtualMaschine>();

            using (computersystems)
            {
                foreach (ManagementObject cs in computersystems.Cast<ManagementObject>())
                {
                    var vm = new VirtualMaschine();

                    var csProperties = cs.GetPropertyHashes();

                    var vmId = cs.GetValue<string>(csProperties, "name")?.Trim();

                    if (Guid.TryParse(vmId, out var vmGuid) is false) continue;

                    vm.Id = vmGuid;
                    vm.ProcessId = cs.GetValue<uint>(csProperties, "processid");
                    vm.Caption = cs.GetValue<string>(csProperties, "caption")?.Trim();
                    vm.Name = cs.GetValue<string>(csProperties, "elementname")?.Trim();
                    vm.Enabled = (EnabledEnum)cs.GetValue<ushort>(csProperties, "enabledstate");
                    vm.EnabledDefault = (EnabledDefaultEnum)cs.GetValue<ushort>(csProperties, "enableddefault");
                    vm.HealthState = (HealthStatusEnum)cs.GetValue<ushort>(csProperties, "healthstate");
                    vm.Status = cs.GetValue<string>(csProperties, "status")?.Trim();
                    vm.OnTime = cs.GetValue<ulong>(csProperties, "ontimeinmilliseconds");
                    vm.ReplicationMode = cs.GetValue<ushort>(csProperties, "replicationmode");
                    vm.ReplicationState = (ReplicationStateEnum)cs.GetValue<ushort>(csProperties, "replicationstate");
                    vm.ReplicationHealth = (ReplicationHealthEnum)cs.GetValue<ushort>(csProperties, "replicationhealth");

                    if (cs.TryGetValue<object>(csProperties, "installdate", out var installdate))
                    {
                        vm.InstallDate = ManagementDateTimeConverter.ToDateTime(installdate?.ToString());
                    }

                    if (cs.TryGetValue<object>(csProperties, "timeoflastconfigurationchange", out var timeoflastconfigurationchange))
                    {
                        vm.TimeOfLastConfigurationChange = ManagementDateTimeConverter.ToDateTime(timeoflastconfigurationchange?.ToString());
                    }

                    if (cs.TryGetValue<object>(csProperties, "timeoflaststatechange", out var timeoflaststatechange))
                    {
                        vm.TimeOfLastStateChange = ManagementDateTimeConverter.ToDateTime(timeoflaststatechange?.ToString());
                    }

                    if (cs.TryGetValue<object>(csProperties, "lastreplicationtime", out var lastreplicationtime))
                    {
                        vm.LastReplicationTime = ManagementDateTimeConverter.ToDateTime(lastreplicationtime?.ToString());
                    }

                    var summaryinformation = cs.GetRelated("msvm_summaryinformation");
                    using (summaryinformation)
                    {
                        foreach (ManagementObject si in summaryinformation.Cast<ManagementObject>())
                        {
                            var siProperties = si.GetPropertyHashes();

                            vm.Notes = si.GetValue<string>(siProperties, "Notes");
                            vm.ConfigurationVersion = si.GetValue<string>(siProperties, "Version");
                            vm.IntegrationServicesVersionState = (IntegrationServicesVersionStateEnum)si.GetValue<ushort>(siProperties, "IntegrationServicesVersionState");
                            vm.GuestOperatingSystem = si.GetValue<string>(siProperties, "GuestOperatingSystem");
                            vm.NumberOfProcessors = si.GetValue<ushort>(siProperties, "NumberOfProcessors");
                            vm.ProcessorLoad = si.GetValue<ushort>(siProperties, "ProcessorLoad");
                            vm.MemoryAvailable = si.GetValue<int>(siProperties, "MemoryAvailable");
                            vm.MemoryUsage = si.GetValue<ulong>(siProperties, "MemoryUsage");
                        }
                    }

                    var virtualSystemSettingData = cs.GetRelated("Msvm_VirtualSystemSettingData");
                    using (virtualSystemSettingData)
                    {
                        var configs = new List<VirtualMaschineConfiguration>();

                        foreach (ManagementObject vssd in virtualSystemSettingData.Cast<ManagementObject>())
                        {
                            var vmc = new VirtualMaschineConfiguration();

                            var vssdProperties = vssd.GetPropertyHashes();

                            var vmcId = vssd.GetValue<string>(vssdProperties, "ConfigurationID")?.Trim();

                            if (Guid.TryParse(vmcId, out var vmcGuid) is false) continue;

                            vmc.Id = vmcGuid.ToString();

                            vmc.Type = vssd.GetValue<string>(vssdProperties, "VirtualSystemType");
                            vmc.Name = vssd.GetValue<string>(vssdProperties, "ElementName");

                            if (vssd.TryGetValue<object>(vssdProperties, "CreationTime", out var creationtime))
                            {
                                vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(creationtime?.ToString());
                            }

                            vmc.Generation = vssd.GetValue<string>(vssdProperties, "VirtualSystemSubType");
                            vmc.Architecture = vssd.GetValue<string>(vssdProperties, "Architecture");
                            vmc.AutomaticStartupAction = (AutomaticStartupActionEnum)vssd.GetValue<uint>(vssdProperties, "AutomaticStartupAction");

                            //if (vssd.TryGetValue<object>(vssdProperties, "AutomaticStartupActionDelay", out var automaticstartupactiondelay))
                            //{
                            //    vmc.CreationTime = ManagementDateTimeConverter.ToDateTime(automaticstartupactiondelay?.ToString());
                            //}

                            vmc.AutomaticShutdownAction = (AutomaticShutdownActionEnum)vssd.GetValue<uint>(vssdProperties, "AutomaticShutdownAction");
                            vmc.AutomaticRecoveryAction = (AutomaticRecoveryActionEnum)vssd.GetValue<uint>(vssdProperties, "AutomaticRecoveryAction");
                            vmc.AutomaticSnapshotsEnabled = vssd.GetValue<bool>(vssdProperties, "AutomaticSnapshotsEnabled");

                            vmc.BaseBoardSerialNumber = vssd.GetValue<string>(vssdProperties, "BaseBoardSerialNumber");
                            vmc.BIOSGUID = vssd.GetValue<string>(vssdProperties, "BIOSGUID");
                            vmc.BIOSSerialNumber = vssd.GetValue<string>(vssdProperties, "BIOSSerialNumber");
                            vmc.BootOrder = vssd.GetValue<ushort[]>(vssdProperties, "BootOrder");
                            vmc.ConfigurationDataRoot = vssd.GetValue<string>(vssdProperties, "ConfigurationDataRoot");
                            vmc.ConfigurationFile = vssd.GetValue<string>(vssdProperties, "ConfigurationFile");
                            vmc.GuestStateDataRoot = vssd.GetValue<string>(vssdProperties, "GuestStateDataRoot");
                            vmc.GuestStateFile = vssd.GetValue<string>(vssdProperties, "GuestStateFile");
                            vmc.SnapshotDataRoot = vssd.GetValue<string>(vssdProperties, "SnapshotDataRoot");
                            vmc.SuspendDataRoot = vssd.GetValue<string>(vssdProperties, "SuspendDataRoot");
                            vmc.SwapFileDataRoot = vssd.GetValue<string>(vssdProperties, "SwapFileDataRoot");
                            vmc.SecureBootEnabled = vssd.GetValue<bool>(vssdProperties, "SecureBootEnabled");
                            vmc.IsAutomaticSnapshot = vssd.GetValue<bool>(vssdProperties, "IsAutomaticSnapshot");
                            vmc.Notes = vssd.GetValue<string[]>(vssdProperties, "Notes");
                            vmc.ParentId = vssd.GetValue<string>(vssdProperties, "Parent");

                            var storageallocationsettingdata = cs.GetRelated("Msvm_StorageAllocationSettingData");
                            using (storageallocationsettingdata)
                            {

                            }

                            configs.Add(vmc);
                        }

                        configs = configs.GroupBy(p => p.Id).Select(p => p.First()).ToList();
                        if (configs.Any(p => p.ParentId is not null))
                        {
                            foreach (var conf in configs.Where(p => p.ParentId is not null))
                            {
                                using var parent = new ManagementObject(conf.ParentId);
                                parent.Get();

                                if (Guid.TryParse(parent["ConfigurationID"]?.ToString(), out var parentGuid) && configs.FirstOrDefault(p => p.Id == parentGuid.ToString()) is VirtualMaschineConfiguration parentConfig)
                                {
                                    conf.ParentId = parentGuid.ToString();

                                    parentConfig.Childs ??= new List<VirtualMaschineConfiguration>();
                                    parentConfig.Childs.Add(conf);
                                }
                                else
                                {
                                    conf.ParentId = null;
                                }
                            }
                        }

                        vm.Configurations = configs.Where(p => p.ParentId is null).ToList();
                    }

                    vms.Add(vm);
                }
            }

            return vms;
        }
    }
}