using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.SystemAlerts;
using EasyNetQ;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Diagnostics;

namespace adas_core.Infrastructure.Services;

public class SendAlertService(
    IOptions<RabbitMqSettings> rabbitMqSettings,
    ILogger<SendAlertService> logger)
    : ISendAlertService
{
    private readonly string _errorObservationsQueue = $"Error{rabbitMqSettings.Value.ObservationsQueue}";
    private readonly string _errorPatientsQueue = $"Error{rabbitMqSettings.Value.PatientsQueue}";
    private readonly string _errorPumpsQueue = $"Error{rabbitMqSettings.Value.PumpsQueue}";
    private readonly string _errorRecordingQueue = $"Error{rabbitMqSettings.Value.RecordingQueue}";
    private readonly string _errorTreatmetsQueue = $"Error{rabbitMqSettings.Value.TreatmentsQueue}";
    private readonly string? _rabbitConnectionString = rabbitMqSettings.Value.ConnectionString;


    //readonly PlatformID[] _windowsPlatforms;

    ////TODO new ws private static DefaultWebSocketHandler defaultWebSocketHandler = new ();        

    //_windowsPlatforms = new[]
    //{
    //    PlatformID.Win32NT,
    //    PlatformID.Win32S,
    //    PlatformID.Win32Windows,
    //    PlatformID.WinCE
    //};

    /// <summary>
    /// Asynchronously retrieves the list of available queues from the Rabbit messaging system.
    /// If the Rabbit connection string is not defined in the web config, an error is logged and an empty list is returned.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Queue"/> objects, or an empty list if the Rabbit connection string is not configured.</returns>
    public async Task<List<Queue>> GetQueues()
        {
            if (_rabbitConnectionString != null) return await GetQueuesAsync();
    
            logger.LogError("Rabbit connection string not defined in web config");
    
            return [];
        }

    /// <summary>
    /// Retrieves performance data from hospitals, including CPU, RAM, and storage consumption metrics.
    /// Logs and suppresses any errors encountered while collecting the data, returning the successfully gathered metrics.
    /// </summary>
    /// <returns>A list of <see cref="Performance"/> entries containing the collected performance metrics; returns an empty list if all collection attempts fail.</returns>
    public List<Performance> GetPerformance()
        {
            logger.LogDebug("Starting check performance data from hospitals");
    
            var list = new List<Performance>();
    
            try
            {
                list.Add(GetConsumedCpu());
                list.Add(GetConsumedRam());
                list.AddRange(GetConsumedStorages());
            }
            catch (Exception e)
            {
                logger.LogError("Error check error performance data from hospitals: {eMessage} {eStackTrace}",
                    e.Message, e.StackTrace);
            }
    
            return list;
        }

    /// <summary>
    /// Asynchronously retrieves a list of API clients by delegating to the underlying data retrieval method.
    /// </summary>
    /// <returns>A task representing the asynchronous operation, containing the list of <see cref="ApiClients"/> instances retrieved.</returns>
    public async Task<List<ApiClients>> GetApiClients()
        {
            return await GetApiClientsAsync();
        }

    /// <summary>
    /// Asynchronously retrieves statistics for the configured RabbitMQ error queues (recording, patients, treatments, observations, and pumps). Only queues with non-empty names are queried, duplicates are ignored, and any exception raised while connecting or fetching stats is logged and results in an empty list being returned.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Queue"/> objects with the name, message count, and consumer count of each queried queue, or an empty list if an error occurs.</returns>
    private async Task<List<Queue>> GetQueuesAsync()
        {
            logger.LogDebug("Starting check rabbitMQ data from hospitals");
    
            var errorQueues = new List<Queue>();
            var errorQueuesNames = new List<string>();
    
            try
            {
                var services = new ServiceCollection();
                services.AddEasyNetQ(_rabbitConnectionString);
    
                var provider = services.BuildServiceProvider();
                var bus = provider.GetRequiredService<IBus>();
    
                var advanced = bus.Advanced;
    
                if (!string.IsNullOrEmpty(_errorRecordingQueue))
                    errorQueuesNames.Add(_errorRecordingQueue);
    
                if (!string.IsNullOrEmpty(_errorPatientsQueue))
                    errorQueuesNames.Add(_errorPatientsQueue);
    
                if (!string.IsNullOrEmpty(_errorTreatmetsQueue))
                    errorQueuesNames.Add(_errorTreatmetsQueue);
    
                if (!string.IsNullOrEmpty(_errorObservationsQueue))
                    errorQueuesNames.Add(_errorObservationsQueue);
    
                if (!string.IsNullOrEmpty(_errorPumpsQueue))
                    errorQueuesNames.Add(_errorPumpsQueue);
    
                foreach (var errorQueueName in errorQueuesNames.Distinct())
                {
                    var stats = await advanced.GetQueueStatsAsync(errorQueueName);
    
                    errorQueues.Add(new Queue
                    {
                        Name = errorQueueName,
                        Messages = stats.MessagesCount,
                        Consumers = stats.ConsumersCount
                    });
                }
            }
            catch (Exception e)
            {
                logger.LogError("Error checking RabbitMQ queues: {msg} {stack}",
                    e.Message, e.StackTrace);
            }
    
            return errorQueues;
        }

    /// <summary>
    /// Retrieves the CPU consumption of the current process as a Performance measurement.
    /// Calculates usage from total processor time divided by processor count, and returns a default Performance instance if the underlying process query fails.
    /// </summary>
    /// <returns>A Performance object describing the CPU usage percentage, total capacity, and unit. If an error occurs, a default Performance instance is returned and the exception is logged.</returns>
    public Performance GetConsumedCpu()
        {
            Performance performance = new();
    
            try
            {
                var currentProcess = Process.GetCurrentProcess();
                var percentage = currentProcess.TotalProcessorTime.TotalMilliseconds / Environment.ProcessorCount / 10;
    
                performance = new Performance
                {
                    Name = "CPU",
                    PercentageConsumed = percentage,
                    ValueTotal = 100,
                    Unit = "%"
                };
            }
            catch (Exception e)
            {
                logger.LogError("Error check CPU performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
            }
    
            return performance;
        }

    /// <summary>
    /// Retrieves the current RAM consumption in gigabytes, returning it as a Performance metric where the total and consumed values are reported as equal with a consumption percentage of 100. If an error occurs while retrieving the memory information, the exception is logged and an empty Performance object is returned.
    /// </summary>
    /// <returns>A Performance object representing the RAM usage in GB; returns an empty Performance object if the memory retrieval fails.</returns>
    public Performance GetConsumedRam()
        {
            Performance performance = new();
            try
            {
                var totalMemoryBytes = GC.GetTotalMemory(false);
                var totalMemoryGb = Math.Round((double)totalMemoryBytes / 1024 / 1024 / 1024, 2);
    
                performance = new Performance
                {
                    Name = "RAM",
                    ValueTotal = totalMemoryGb,
                    ValueConsumed = totalMemoryGb,
                    PercentageConsumed = 100,
                    Unit = "GB"
                };
            }
            catch (Exception e)
            {
                logger.LogError("Error check RAM performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
            }
    
            return performance;
        }


    /// <summary>
    /// Calculates the consumed storage for the specified drive, returning total and consumed values in gigabytes along with the consumption percentage. If the drive's total size is zero, the percentage is not computed and the total remains zero. Any errors encountered during calculation are logged and an empty <see cref="Performance"/> object is returned.
    /// </summary>
    /// <param name="drive">The drive whose storage consumption is to be measured.</param>
    /// <returns>A <see cref="Performance"/> instance containing the consumed storage, total storage, percentage consumed, and the unit (GB) for the drive.</returns>
    public Performance GetConsumedStorage(DriveInfo drive)
        {
            Performance performance = new();
            try
            {
                double percentage = 0;
    
                //Total storage
                double valueTotal = drive.TotalSize;
    
                //Consumed storage
                var value = valueTotal - drive.AvailableFreeSpace;
    
                if (valueTotal != 0)
                {
                    percentage = Math.Round(value * 100 / valueTotal, 2); //%
    
                    valueTotal = Math.Round(valueTotal / 1024 / 1024 / 1024, 2); //Bytes -> GB
                }
    
                value = Math.Round(value / 1024 / 1024 / 1024, 2); //Bytes -> GB
    
                performance = new Performance
                {
                    Name = "STORAGE " + drive.Name,
                    ValueConsumed = value,
                    ValueTotal = valueTotal,
                    PercentageConsumed = percentage,
                    Unit = "GB"
                };
            }
            catch (Exception e)
            {
                logger.LogError("Error check Storage performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
            }
    
            return performance;
        }

    /// <summary>
    /// Retrieves the consumed storage performance information for all ready drives on the system,
    /// skipping any drive that is not ready and logging errors encountered while processing
    /// individual drives or the overall enumeration without halting the operation.
    /// </summary>
    /// <returns>A list of <see cref="Performance"/> entries describing the consumed storage for each
    /// successfully processed drive; returns an empty list if no drives yield results or if an
    /// error occurs during enumeration.</returns>
    private List<Performance> GetConsumedStorages()
        {
            var list = new List<Performance>();
            try
            {
                foreach (var drive in DriveInfo.GetDrives())
                {
                    if (!drive.IsReady) continue;
                    try
                    {
                        var performance = GetConsumedStorage(drive);
                        list.Add(performance);
                    }
                    catch (Exception e)
                    {
                        logger.LogError("Error check Storage drive {drive} in performance: {eMessage}", drive.Name,
                            e.Message);
                    }
                }
            }
            catch (Exception e)
            {
                logger.LogError("Error check Storages performance: {eMessage}", e.Message);
            }
    
            return list;
        }


    /// <summary>
    /// Asynchronously retrieves the list of connected API clients (WebSocket clients) from hospitals.
    /// Returns an empty list if the underlying call returns null or fails; any exception is caught and logged without being rethrown.
    /// </summary>
    /// <returns>A task that resolves to the list of connected API clients, which may be empty if no clients were retrieved.</returns>
    private async Task<List<ApiClients>> GetApiClientsAsync()
        {
            logger.LogDebug("Starting check conected clients from hospitals");
    
            var clientsList = new List<ApiClients>();
    
            try
            {
                var clients = await GetWebSocketClients();
                if (clients != null)
                    clientsList.Add(clients);
            }
            catch (Exception e)
            {
                logger.LogError("Error check error conected clients from hospitals: {eMessage} {eStackTrace}",
                    e.Message, e.StackTrace);
            }
    
            return clientsList;
        }

    /// <summary>
    /// Asynchronously retrieves the list of connected WebSocket API clients, handling any errors by logging them and returning an empty <see cref="ApiClients"/> instance as a fallback.
    /// </summary>
    /// <returns>A <see cref="Task{TResult}"/> that resolves to an <see cref="ApiClients"/> instance representing the connected WebSocket subscribers.</returns>
    private Task<ApiClients?> GetWebSocketClients()
        {
            ApiClients apiClients = new();
    
            try
            {
                //TODO new ws apiClients = defaultWebSocketHandler.GetSubscribersConected();
    
                //apiClients = webSocketHandler.GetSubscribersConected();                
            }
            catch (Exception e)
            {
                logger.LogError("Error check WebSocket Clients: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
            }
    
            return Task.FromResult<ApiClients?>(apiClients);
        }
}