using System.Diagnostics;

namespace ZB.MOM.NatsNet.Server.Internal;

/// <summary>
/// Provides cross-platform process CPU and memory usage statistics.
/// Mirrors the Go <c>pse</c> (Process Status Emulation) package, replacing
/// per-platform implementations (rusage, /proc/stat, PDH) with
/// <see cref="System.Diagnostics.Process"/>.
/// </summary>
public static class ProcessStatsProvider
{
    private static readonly Process _self = Process.GetCurrentProcess();
    private static readonly int _processorCount = Environment.ProcessorCount;
    private static readonly object _lock = new();

    private static TimeSpan _lastCpuTime;
    private static DateTime _lastSampleTime;
    private static double _cachedPcpu;
    private static long _cachedRss;
    private static long _cachedVss;

    static ProcessStatsProvider()
    {
        UpdateUsage();
        StartPeriodicSampling();
    }

    /// <summary>
    /// Returns the current process CPU percentage, RSS (bytes), and VSS (bytes).
    /// Values are refreshed approximately every second by a background timer.
    /// </summary>
    /// <param name="pcpu">Percent CPU utilization (0–100 × core count).</param>
    /// <param name="rss">Resident set size in bytes.</param>
    /// <param name="vss">Virtual memory size in bytes.</param>
    public static void ProcUsage(out double pcpu, out long rss, out long vss)
    {
        lock (_lock)
        {
            pcpu = _cachedPcpu;
            rss = _cachedRss;
            vss = _cachedVss;
        }
    }

    private static void UpdateUsage()
    {
        try
        {
            _self.Refresh();
            var now = DateTime.UtcNow;
            var cpuTime = _self.TotalProcessorTime;

            lock (_lock)
            {
                var elapsed = now - _lastSampleTime;
                if (elapsed >= TimeSpan.FromMilliseconds(500))
                {
                    var cpuDelta = (cpuTime - _lastCpuTime).TotalSeconds;
                    // Normalize against elapsed wall time.
                    // Result is 0–100; does not multiply by ProcessorCount to match Go behaviour.
                    _cachedPcpu = elapsed.TotalSeconds > 0
                        ? Math.Round(cpuDelta / elapsed.TotalSeconds * 1000.0) / 10.0
                        : 0;
                    _lastSampleTime = now;
                    _lastCpuTime = cpuTime;
                }

                _cachedRss = _self.WorkingSet64;
                _cachedVss = _self.VirtualMemorySize64;
            }
        }
        catch
        {
            // Suppress — diagnostics should never crash the server.
        }
    }

    private static void StartPeriodicSampling()
    {
        var timer = new Timer(_ => UpdateUsage(), null,
            dueTime: TimeSpan.FromSeconds(1),
            period: TimeSpan.FromSeconds(1));

        // Keep timer alive for the process lifetime.
        GC.KeepAlive(timer);
    }

    // --- Windows PDH helpers (replaced by Process class in .NET) ---
    // The following methods exist to satisfy the porting mapping but delegate
    // to the cross-platform Process API above.

    internal static string GetProcessImageName() =>
        Path.GetFileNameWithoutExtension(Environment.ProcessPath ?? _self.ProcessName);

    internal static void InitCounters()
    {
        // No-op: .NET Process class initializes lazily.
    }

    internal static double PdhOpenQuery() => 0;      // Mapped to Process API.
    internal static double PdhAddCounter() => 0;
    internal static double PdhCollectQueryData() => 0;
    internal static double PdhGetFormattedCounterArrayDouble() => 0;
    internal static double GetCounterArrayData() => 0;
}
