using System.Runtime.InteropServices;

namespace ZB.MOM.NatsNet.Server.Internal;

/// <summary>
/// Returns total physical memory available to the system in bytes.
/// Mirrors the Go <c>sysmem</c> package with platform-specific implementations.
/// Returns 0 if the value cannot be determined on the current platform.
/// </summary>
public static class SystemMemory
{
    /// <summary>Returns total physical memory in bytes, or 0 on failure.</summary>
    public static long Memory()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return MemoryWindows();
        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            return MemoryDarwin();
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            return MemoryLinux();
        return 0;
    }

    // --- macOS ---

    internal static long MemoryDarwin() => SysctlInt64("hw.memsize");

    /// <summary>
    /// Reads an int64 sysctl value by name on BSD-derived systems (macOS, FreeBSD, etc.).
    /// </summary>
    internal static unsafe long SysctlInt64(string name)
    {
        var size = (nuint)sizeof(long);
        long value = 0;
        var ret = sysctlbyname(name, &value, &size, IntPtr.Zero, 0);
        return ret == 0 ? value : 0;
    }

    [DllImport("libc", EntryPoint = "sysctlbyname", SetLastError = true)]
    private static extern unsafe int sysctlbyname(
        string name,
        void* oldp,
        nuint* oldlenp,
        IntPtr newp,
        nuint newlen);

    // --- Linux ---

    internal static long MemoryLinux()
    {
        try
        {
            // Parse MemTotal from /proc/meminfo (value is in kB).
            foreach (var line in File.ReadLines("/proc/meminfo"))
            {
                if (!line.StartsWith("MemTotal:", StringComparison.Ordinal))
                    continue;
                var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length >= 2 && long.TryParse(parts[1], out var kb))
                    return kb * 1024L;
            }
        }
        catch
        {
            // Fall through to return 0.
        }
        return 0;
    }

    // --- Windows ---

    [StructLayout(LayoutKind.Sequential)]
    private struct MemoryStatusEx
    {
        public uint dwLength;
        public uint dwMemoryLoad;
        public ulong ullTotalPhys;
        public ulong ullAvailPhys;
        public ulong ullTotalPageFile;
        public ulong ullAvailPageFile;
        public ulong ullTotalVirtual;
        public ulong ullAvailVirtual;
        public ulong ullAvailExtendedVirtual;
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GlobalMemoryStatusEx(ref MemoryStatusEx lpBuffer);

    internal static long MemoryWindows()
    {
        var msx = new MemoryStatusEx { dwLength = (uint)Marshal.SizeOf<MemoryStatusEx>() };
        return GlobalMemoryStatusEx(ref msx) ? (long)msx.ullTotalPhys : 0;
    }
}
