using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;

namespace NexusShell.Services;

public sealed class SystemStatusSnapshot
{
    public string SystemStatus { get; init; } = "OPTIMALT";
    public string NetworkLabel { get; init; } = "—";
    public int CpuPercent { get; init; }
    public int MemoryLoadPercent { get; init; }
    public string MemoryLabel { get; init; } = "—";
    public string StorageLabel { get; init; } = "—";
    public string IdentityLatencyLabel { get; init; } = "—";
    public string AgentStatusLabel { get; init; } = "Klar";
    public int HealthPercent { get; init; } = 100;
    public bool ServicesOk { get; init; } = true;
    public bool NetworkOk { get; init; } = true;
    public bool SecurityOk { get; init; } = true;
    public bool IsOnline { get; init; }
}

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

    private ulong _prevIdleTicks;
    private ulong _prevKernelTicks;
    private ulong _prevUserTicks;
    private bool _hasCpuBaseline;
    private int _lastCpuPercent;
    private string _identityLatencyLabel = "—";

    public string IdentityLatencyLabel => _identityLatencyLabel;

    public SystemStatusSnapshot Capture()
    {
        var online = NetworkInterface.GetIsNetworkAvailable();
        var cpu = SampleCpuUsagePercent();
        var memoryLoad = SampleMemoryLoadPercent();
        var memoryLabel = GetMemoryLabel(out _);
        var storage = GetStorageLabel(out var freeGb);
        var network = GetNetworkLabel(online);
        var load = Math.Clamp(Math.Max(cpu, memoryLoad), 0, 100);

        return new SystemStatusSnapshot
        {
            SystemStatus = ResolveSystemStatus(online, load, freeGb),
            NetworkLabel = network,
            CpuPercent = cpu,
            MemoryLoadPercent = memoryLoad,
            MemoryLabel = memoryLabel,
            StorageLabel = storage,
            IdentityLatencyLabel = _identityLatencyLabel,
            AgentStatusLabel = "Klar",
            HealthPercent = ResolveHealthPercent(online, load, freeGb),
            IsOnline = online,
            NetworkOk = online,
            ServicesOk = online && load < 95,
            SecurityOk = true,
        };
    }

    public async Task RefreshIdentityLatencyAsync(
        string authority = IdentityHostService.DefaultAuthority,
        CancellationToken cancellationToken = default)
    {
        var sw = Stopwatch.StartNew();
        try
        {
            using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(2) };
            using var response = await client.GetAsync($"{authority.TrimEnd('/')}/health", cancellationToken);
            _identityLatencyLabel = response.IsSuccessStatusCode
                ? $"{sw.ElapsedMilliseconds} ms"
                : "Offline";
        }
        catch (OperationCanceledException)
        {
            throw;
        }
        catch
        {
            _identityLatencyLabel = "Offline";
        }
    }

    private int SampleCpuUsagePercent()
    {
        if (!GetSystemTimes(out var idle, out var kernel, out var user))
        {
            return _lastCpuPercent;
        }

        var idleTicks = FileTimeToUInt64(idle);
        var kernelTicks = FileTimeToUInt64(kernel);
        var userTicks = FileTimeToUInt64(user);

        if (!_hasCpuBaseline)
        {
            _prevIdleTicks = idleTicks;
            _prevKernelTicks = kernelTicks;
            _prevUserTicks = userTicks;
            _hasCpuBaseline = true;
            return _lastCpuPercent;
        }

        var idleDelta = idleTicks - _prevIdleTicks;
        var totalDelta = (kernelTicks + userTicks) - (_prevKernelTicks + _prevUserTicks);
        _prevIdleTicks = idleTicks;
        _prevKernelTicks = kernelTicks;
        _prevUserTicks = userTicks;

        if (totalDelta == 0)
        {
            return _lastCpuPercent;
        }

        var used = totalDelta - idleDelta;
        _lastCpuPercent = (int)Math.Clamp(used * 100d / totalDelta, 0, 100);
        return _lastCpuPercent;
    }

    private static int SampleMemoryLoadPercent()
    {
        var status = new MemoryStatusEx { dwLength = (uint)Marshal.SizeOf<MemoryStatusEx>() };
        return GlobalMemoryStatusEx(ref status)
            ? (int)Math.Clamp(status.dwMemoryLoad, 0, 100)
            : 0;
    }

    private static string GetMemoryLabel(out int loadPercent)
    {
        loadPercent = SampleMemoryLoadPercent();
        var status = new MemoryStatusEx { dwLength = (uint)Marshal.SizeOf<MemoryStatusEx>() };
        if (!GlobalMemoryStatusEx(ref status) || status.ullTotalPhys == 0)
        {
            return "—";
        }

        var usedBytes = status.ullTotalPhys - status.ullAvailPhys;
        var usedGb = usedBytes / (1024d * 1024 * 1024);
        var totalGb = status.ullTotalPhys / (1024d * 1024 * 1024);
        return $"{usedGb.ToString("0.#", DaDk)} / {totalGb.ToString("0.#", DaDk)} GB";
    }

    private static string ResolveSystemStatus(bool online, int performance, double freeGb)
    {
        if (!online)
        {
            return "BEGRÆNSET";
        }

        if (freeGb >= 0 && freeGb < 10)
        {
            return "LAV PLADS";
        }

        return performance switch
        {
            >= 90 => "BELASTET",
            >= 70 => "AKTIV",
            _ => "OPTIMALT",
        };
    }

    private static int ResolveHealthPercent(bool online, int performance, double freeGb)
    {
        if (!online)
        {
            return 55;
        }

        var score = 100;
        score -= Math.Max(0, performance - 50) / 2;
        if (freeGb >= 0 && freeGb < 20)
        {
            score -= 12;
        }

        return Math.Clamp(score, 40, 100);
    }

    private static string GetNetworkLabel(bool online)
    {
        if (!online)
        {
            return "Offline";
        }

        var iface = GetPrimaryInterface();
        if (iface is null)
        {
            return "Ingen forbindelse";
        }

        if (iface.Speed > 0)
        {
            return FormatLinkSpeed(iface.Speed);
        }

        return iface.NetworkInterfaceType switch
        {
            NetworkInterfaceType.Wireless80211 => "Wi‑Fi",
            NetworkInterfaceType.Ethernet => "Ethernet",
            _ => "Tilsluttet",
        };
    }

    private static NetworkInterface? GetPrimaryInterface()
    {
        return 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 FormatLinkSpeed(long bitsPerSecond)
    {
        var gbps = bitsPerSecond / 1_000_000_000d;
        if (gbps >= 1)
        {
            return $"{gbps.ToString("0.#", DaDk)} Gbps";
        }

        var mbps = bitsPerSecond / 1_000_000d;
        if (mbps >= 1)
        {
            return $"{mbps.ToString("0", DaDk)} Mbps";
        }

        var kbps = bitsPerSecond / 1_000d;
        return kbps >= 1 ? $"{kbps.ToString("0", DaDk)} Kbps" : "Tilsluttet";
    }

    private static string GetStorageLabel(out double freeGb)
    {
        freeGb = -1;
        try
        {
            var drive = ResolveSystemDrive();
            if (drive is null)
            {
                return "—";
            }

            freeGb = drive.AvailableFreeSpace / (1024d * 1024 * 1024);
            if (freeGb >= 100)
            {
                return $"{freeGb.ToString("0", DaDk)} GB ledig";
            }

            if (freeGb >= 1)
            {
                return $"{freeGb.ToString("0.#", DaDk)} GB ledig";
            }

            var freeMb = drive.AvailableFreeSpace / (1024d * 1024);
            return $"{freeMb.ToString("0", DaDk)} MB ledig";
        }
        catch
        {
            return "—";
        }
    }

    private static DriveInfo? ResolveSystemDrive()
    {
        var systemRoot = Path.GetPathRoot(Environment.SystemDirectory);
        if (!string.IsNullOrWhiteSpace(systemRoot))
        {
            var systemDrive = new DriveInfo(systemRoot);
            if (systemDrive.IsReady)
            {
                return systemDrive;
            }
        }

        return DriveInfo.GetDrives()
            .FirstOrDefault(static d => d is { IsReady: true, DriveType: DriveType.Fixed });
    }

    private static ulong FileTimeToUInt64(FileTime time) =>
        ((ulong)time.dwHighDateTime << 32) | time.dwLowDateTime;

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetSystemTimes(out FileTime idleTime, out FileTime kernelTime, out FileTime userTime);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GlobalMemoryStatusEx(ref MemoryStatusEx status);

    [StructLayout(LayoutKind.Sequential)]
    private struct FileTime
    {
        public uint dwLowDateTime;
        public uint dwHighDateTime;
    }

    [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;
    }
}
