using System.Globalization;
using System.Net.NetworkInformation;
using NexusShell.Agent.Models;
using NexusShell.Models;
using NexusShell.Services;

namespace NexusShell.Agent;

public sealed class NexusInventoryService
{
    private static readonly CultureInfo DaDk = CultureInfo.GetCultureInfo("da-DK");
    private readonly NexusHardwareService _hardware;

    public NexusInventoryService(NexusHardwareService hardware)
    {
        _hardware = hardware;
    }

    public NexusAgentInventoryResponse CaptureSnapshot()
    {
        var deviceInfo = _hardware.CaptureDeviceInfo();
        var hardware = _hardware.CaptureHardwareOverview();
        var memory = _hardware.CaptureMemory();
        var volumes = _hardware.GetVolumes();
        var network = _hardware.CaptureNetwork();
        var gpu = _hardware.CaptureGpu();
        var displays = _hardware.GetDisplays();
        var printers = _hardware.GetPrinters();
        var usb = _hardware.GetUsbDevices();
        var (btStatus, btDevices) = _hardware.CaptureBluetooth();

        var primaryVolume = volumes.FirstOrDefault();
        var storage = primaryVolume is not null
            ? $"{primaryVolume.FriendlyLabel} — {primaryVolume.FreeGb.ToString("0.#", DaDk)} / {primaryVolume.TotalGb.ToString("0.#", DaDk)} GB ledig"
            : deviceInfo.DiskLabel;

        return new NexusAgentInventoryResponse
        {
            CapturedAtUtc = DateTimeOffset.UtcNow,
            ComputerName = hardware.MachineName,
            Os = hardware.OsLabel,
            Cpu = deviceInfo.CpuName,
            Ram = $"{memory.TotalGb.ToString("0.#", DaDk)} GB ({memory.LoadPercent}% brugt)",
            Storage = storage,
            Gpu = gpu.IsAvailable ? gpu.Name : deviceInfo.GpuName,
            NetworkAdapters = GetNetworkAdapters(network),
            Displays = displays.Select(static d => new NexusAgentDisplayInfo
            {
                Name = d.Name,
                Resolution = d.Resolution,
                RefreshRate = d.RefreshRate,
                IsPrimary = d.IsPrimary,
            }).ToList(),
            Printers = printers.Select(static p => new NexusAgentPrinterInfo { Name = p.Name }).ToList(),
            UsbDevices = usb.Select(static u => new NexusAgentUsbInfo
            {
                Name = u.Name,
                DeviceType = u.DeviceType,
                Manufacturer = u.Manufacturer,
            }).ToList(),
            BluetoothStatus = btStatus,
            BluetoothDevices = btDevices.Select(static b => b.Name).ToList(),
            SystemManufacturer = hardware.SystemManufacturer,
            SystemModel = hardware.SystemModel,
            BiosVersion = hardware.BiosVersion,
        };
    }

    private static IReadOnlyList<NexusAgentNetworkAdapterInfo> GetNetworkAdapters(NexusNetworkSnapshot active)
    {
        try
        {
            return NetworkInterface.GetAllNetworkInterfaces()
                .Where(static n => n.NetworkInterfaceType is not (NetworkInterfaceType.Loopback or NetworkInterfaceType.Tunnel))
                .Select(n =>
                {
                    var ipv4 = n.GetIPProperties().UnicastAddresses
                        .FirstOrDefault(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)?
                        .Address.ToString() ?? "—";

                    return new NexusAgentNetworkAdapterInfo
                    {
                        Name = n.Name,
                        Type = n.NetworkInterfaceType.ToString(),
                        Status = n.OperationalStatus.ToString(),
                        IpAddress = ipv4,
                    };
                })
                .OrderByDescending(static n => n.Status == "Up")
                .ThenBy(static n => n.Name)
                .ToList();
        }
        catch
        {
            return
            [
                new NexusAgentNetworkAdapterInfo
                {
                    Name = active.ActiveConnection,
                    Type = "Active",
                    Status = active.IsOnline ? "Up" : "Down",
                    IpAddress = active.IpAddress,
                },
            ];
        }
    }
}
