using System.Globalization;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using NexusShell.Models;

namespace NexusShell.Services;

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

    private long _prevBytesReceived;
    private long _prevBytesSent;
    private DateTime _prevSampleUtc = DateTime.UtcNow;
    private bool _hasBaseline;
    private double _downloadBps;
    private double _uploadBps;

    private readonly NexusNetworkInfoService _infoService;

    public NexusNetworkService(NexusNetworkInfoService infoService)
    {
        _infoService = infoService;
    }

    public NexusNetworkSnapshot Capture()
    {
        SampleThroughput();
        var info = _infoService.Capture();
        var iface = GetPrimaryInterface();

        return new NexusNetworkSnapshot
        {
            ActiveConnection = info.ActiveNetwork,
            IpAddress = info.IpAddress,
            Gateway = iface is null ? "—" : GetGateway(iface),
            DownloadSpeed = FormatSpeed(_downloadBps),
            UploadSpeed = FormatSpeed(_uploadBps),
            IsOnline = info.IsOnline,
        };
    }

    private void SampleThroughput()
    {
        var iface = GetPrimaryInterface();
        if (iface is null)
        {
            _downloadBps = 0;
            _uploadBps = 0;
            return;
        }

        var stats = iface.GetIPv4Statistics();
        var now = DateTime.UtcNow;
        var elapsed = (now - _prevSampleUtc).TotalSeconds;

        if (!_hasBaseline || elapsed <= 0.05)
        {
            _prevBytesReceived = stats.BytesReceived;
            _prevBytesSent = stats.BytesSent;
            _prevSampleUtc = now;
            _hasBaseline = true;
            return;
        }

        var downDelta = Math.Max(0, stats.BytesReceived - _prevBytesReceived);
        var upDelta = Math.Max(0, stats.BytesSent - _prevBytesSent);
        _downloadBps = downDelta / elapsed;
        _uploadBps = upDelta / elapsed;

        _prevBytesReceived = stats.BytesReceived;
        _prevBytesSent = stats.BytesSent;
        _prevSampleUtc = now;
    }

    private static NetworkInterface? GetPrimaryInterface() =>
        NetworkInterface.GetAllNetworkInterfaces()
            .Where(static n => n.OperationalStatus == OperationalStatus.Up)
            .Where(static n => n.NetworkInterfaceType is not (NetworkInterfaceType.Loopback or NetworkInterfaceType.Tunnel))
            .Where(static n => n.GetIPProperties().GatewayAddresses.Count > 0 || n.Speed > 0)
            .OrderByDescending(static n => n.Speed)
            .ThenByDescending(static n => n.GetIPProperties().GatewayAddresses.Count)
            .FirstOrDefault();

    private static string GetGateway(NetworkInterface iface)
    {
        var gateway = iface.GetIPProperties().GatewayAddresses
            .FirstOrDefault(static a => a.Address.AddressFamily == AddressFamily.InterNetwork)?.Address;
        return gateway?.ToString() ?? "—";
    }

    private static string FormatSpeed(double bytesPerSecond)
    {
        if (bytesPerSecond <= 0)
        {
            return "0 B/s";
        }

        var kb = bytesPerSecond / 1024d;
        if (kb < 1024)
        {
            return $"{kb.ToString("0.#", DaDk)} KB/s";
        }

        var mb = kb / 1024d;
        if (mb < 1024)
        {
            return $"{mb.ToString("0.#", DaDk)} MB/s";
        }

        return $"{(mb / 1024d).ToString("0.##", DaDk)} GB/s";
    }
}
