using System.IO;
using System.Runtime.InteropServices;

namespace AxCopilot.Services;

/// <summary>
/// Phase L3-9: 시스템 성능 모니터 서비스.
/// CPU(GetSystemTimes P/Invoke), RAM(GlobalMemoryStatusEx), Disk C(DriveInfo) 수집.
/// 싱글턴 — 런처 표시 시 StartPolling(), 숨김 시 StopPolling().
/// </summary>
internal sealed class PerformanceMonitorService
{
    public static readonly PerformanceMonitorService Instance = new();

    // ─── P/Invoke ────────────────────────────────────────────────────────────

    [StructLayout(LayoutKind.Sequential)]
    private struct FILETIME { public uint Low; public uint High; }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct MEMORYSTATUSEX
    {
        public uint  dwLength;
        public uint  dwMemoryLoad;   // % in use
        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)]
    private static extern bool GetSystemTimes(out FILETIME idle, out FILETIME kernel, out FILETIME user);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);

    // ─── 공개 프로퍼티 ────────────────────────────────────────────────────────

    public double CpuPercent  { get; private set; }
    public double RamPercent  { get; private set; }
    public string RamText     { get; private set; } = "";
    public double DiskCPercent { get; private set; }
    public string DiskCText   { get; private set; } = "";

    // ─── 내부 상태 ───────────────────────────────────────────────────────────

    private System.Threading.Timer? _timer;
    private FILETIME _prevIdle, _prevKernel, _prevUser;
    private bool _hasPrev;

    private PerformanceMonitorService() { }

    public void StartPolling()
    {
        if (_timer != null) return;
        // 즉시 첫 샘플 + 이후 2초마다 갱신
        _timer = new System.Threading.Timer(_ => Sample(), null, 0, 2000);
    }

    public void StopPolling()
    {
        _timer?.Dispose();
        _timer = null;
        _hasPrev = false;
    }

    // ─── 샘플링 ──────────────────────────────────────────────────────────────

    private void Sample()
    {
        SampleCpu();
        SampleRam();
        SampleDisk();
    }

    private void SampleCpu()
    {
        try
        {
            if (!GetSystemTimes(out var idle, out var kernel, out var user)) return;

            if (_hasPrev)
            {
                var idleDelta   = ToUlong(idle)   - ToUlong(_prevIdle);
                var kernelDelta = ToUlong(kernel) - ToUlong(_prevKernel);
                var userDelta   = ToUlong(user)   - ToUlong(_prevUser);

                var total = kernelDelta + userDelta;
                var busy  = total - idleDelta;
                CpuPercent = total > 0 ? Math.Max(0, Math.Min(100, 100.0 * busy / total)) : 0;
            }

            _prevIdle   = idle;
            _prevKernel = kernel;
            _prevUser   = user;
            _hasPrev    = true;
        }
        catch { /* 오류 무시 */ }
    }

    private void SampleRam()
    {
        try
        {
            var mem = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf<MEMORYSTATUSEX>() };
            if (!GlobalMemoryStatusEx(ref mem)) return;

            RamPercent = mem.dwMemoryLoad;
            var usedGB  = (mem.ullTotalPhys - mem.ullAvailPhys) / (1024.0 * 1024 * 1024);
            var totalGB = mem.ullTotalPhys / (1024.0 * 1024 * 1024);
            RamText = $"{usedGB:F1}/{totalGB:F0}GB";
        }
        catch { /* 오류 무시 */ }
    }

    private void SampleDisk()
    {
        try
        {
            var drive = DriveInfo.GetDrives()
                .FirstOrDefault(d => d.IsReady &&
                    d.Name.StartsWith("C", StringComparison.OrdinalIgnoreCase));
            if (drive == null) return;

            var usedBytes  = drive.TotalSize - drive.AvailableFreeSpace;
            DiskCPercent   = 100.0 * usedBytes / drive.TotalSize;
            var usedGB     = usedBytes / (1024.0 * 1024 * 1024);
            var totalGB    = drive.TotalSize / (1024.0 * 1024 * 1024);
            DiskCText      = $"C:{usedGB:F0}/{totalGB:F0}GB";
        }
        catch { /* 오류 무시 */ }
    }

    private static ulong ToUlong(FILETIME ft) => ((ulong)ft.High << 32) | ft.Low;
}
