using System.Diagnostics;
using System.Globalization;
using NexusShell.Models;

namespace NexusShell.Services;

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

    private readonly Dictionary<int, (TimeSpan Total, DateTime SampleUtc)> _samples = new();
    private DateTime _lastCleanupUtc = DateTime.UtcNow;

    public IReadOnlyList<NexusProcessInfo> GetTopProcesses(int count = 12)
    {
        CleanupStaleSamples();
        var now = DateTime.UtcNow;
        var processes = Process.GetProcesses();
        var ranked = new List<(Process Process, double CpuPercent, long MemoryBytes)>();

        try
        {
            foreach (var process in processes)
            {
                try
                {
                    var memory = process.WorkingSet64;
                    var cpu = SampleCpuPercent(process, now);
                    ranked.Add((process, cpu, memory));
                }
                catch
                {
                    process.Dispose();
                }
            }
        }
        finally
        {
            foreach (var entry in ranked)
            {
                entry.Process.Dispose();
            }
        }

        return ranked
            .OrderByDescending(static p => p.CpuPercent)
            .ThenByDescending(static p => p.MemoryBytes)
            .Take(count)
            .Select(static p => new NexusProcessInfo
            {
                Name = p.Process.ProcessName,
                ProcessId = p.Process.Id,
                CpuPercent = Math.Round(p.CpuPercent, 1),
                MemoryLabel = FormatMemory(p.MemoryBytes),
            })
            .ToList();
    }

    private double SampleCpuPercent(Process process, DateTime nowUtc)
    {
        try
        {
            var total = process.TotalProcessorTime;
            if (!_samples.TryGetValue(process.Id, out var previous))
            {
                _samples[process.Id] = (total, nowUtc);
                return 0;
            }

            var elapsed = (nowUtc - previous.SampleUtc).TotalMilliseconds;
            if (elapsed <= 0)
            {
                return 0;
            }

            var cpuUsed = (total - previous.Total).TotalMilliseconds;
            _samples[process.Id] = (total, nowUtc);
            var cpuPercent = cpuUsed / (Environment.ProcessorCount * elapsed) * 100d;
            return Math.Clamp(cpuPercent, 0, 100);
        }
        catch
        {
            return 0;
        }
    }

    private void CleanupStaleSamples()
    {
        if ((DateTime.UtcNow - _lastCleanupUtc).TotalSeconds < 30)
        {
            return;
        }

        _lastCleanupUtc = DateTime.UtcNow;
        var stale = _samples
            .Where(static pair => (DateTime.UtcNow - pair.Value.SampleUtc).TotalSeconds > 60)
            .Select(static pair => pair.Key)
            .ToList();

        foreach (var id in stale)
        {
            _samples.Remove(id);
        }
    }

    private static string FormatMemory(long bytes)
    {
        var mb = bytes / (1024d * 1024);
        if (mb >= 1024)
        {
            return $"{(mb / 1024d).ToString("0.#", DaDk)} GB";
        }

        return $"{mb.ToString("0", DaDk)} MB";
    }
}
