//go:build windows

// Package metrics reads host CPU / RAM / disk usage on Windows using only the
// stdlib (kernel32 via syscall) — no external dependency (GOPROXY is unavailable
// in this environment, and gopsutil isn't needed for these three numbers).
package metrics

import (
	"runtime"
	"syscall"
	"time"
	"unsafe"
)

var (
	kernel32                 = syscall.NewLazyDLL("kernel32.dll")
	procGlobalMemoryStatusEx = kernel32.NewProc("GlobalMemoryStatusEx")
	procGetSystemTimes       = kernel32.NewProc("GetSystemTimes")
	procGetDiskFreeSpaceExW  = kernel32.NewProc("GetDiskFreeSpaceExW")
)

type memoryStatusEx struct {
	cbSize                  uint32
	dwMemoryLoad            uint32
	ullTotalPhys            uint64
	ullAvailPhys            uint64
	ullTotalPageFile        uint64
	ullAvailPageFile        uint64
	ullTotalVirtual         uint64
	ullAvailVirtual         uint64
	ullAvailExtendedVirtual uint64
}

type filetime struct{ low, high uint32 }

func (f filetime) u64() uint64 { return uint64(f.high)<<32 | uint64(f.low) }

type cpuSample struct{ idle, kernel, user uint64 }

func readCPU() (cpuSample, bool) {
	var idle, kernel, user filetime
	r, _, _ := procGetSystemTimes.Call(
		uintptr(unsafe.Pointer(&idle)),
		uintptr(unsafe.Pointer(&kernel)),
		uintptr(unsafe.Pointer(&user)),
	)
	if r == 0 {
		return cpuSample{}, false
	}
	return cpuSample{idle.u64(), kernel.u64(), user.u64()}, true
}

// CPUPercent samples system CPU times over `interval` and returns busy % (0-100).
// On Windows the "kernel" time INCLUDES idle, so total = kernel+user, busy = total-idle.
func CPUPercent(interval time.Duration) int {
	a, ok := readCPU()
	if !ok {
		return 0
	}
	time.Sleep(interval)
	b, ok := readCPU()
	if !ok {
		return 0
	}
	idleD := float64(b.idle - a.idle)
	totalD := float64((b.kernel - a.kernel) + (b.user - a.user))
	if totalD <= 0 {
		return 0
	}
	busy := (totalD - idleD) / totalD * 100
	if busy < 0 {
		busy = 0
	}
	if busy > 100 {
		busy = 100
	}
	return int(busy + 0.5)
}

// Memory returns (totalMB, availableMB).
func Memory() (totalMB, availMB int) {
	var m memoryStatusEx
	m.cbSize = uint32(unsafe.Sizeof(m))
	if r, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&m))); r == 0 {
		return 0, 0
	}
	return int(m.ullTotalPhys / 1024 / 1024), int(m.ullAvailPhys / 1024 / 1024)
}

// Disk returns (usedPct, totalGB) for the volume containing path (default C:\).
func Disk(path string) (usedPct, totalGB int) {
	if path == "" {
		path = "C:\\"
	}
	p, err := syscall.UTF16PtrFromString(path)
	if err != nil {
		return 0, 0
	}
	var freeAvail, total, totalFree uint64
	r, _, _ := procGetDiskFreeSpaceExW.Call(
		uintptr(unsafe.Pointer(p)),
		uintptr(unsafe.Pointer(&freeAvail)),
		uintptr(unsafe.Pointer(&total)),
		uintptr(unsafe.Pointer(&totalFree)),
	)
	if r == 0 || total == 0 {
		return 0, 0
	}
	used := total - totalFree
	return int(float64(used)/float64(total)*100 + 0.5), int(total / 1024 / 1024 / 1024)
}

// Cores returns the logical CPU count.
func Cores() int { return runtime.NumCPU() }
