//go:build windows

package api

import (
	"sync"
	"syscall"
	"unsafe"

	"golang.org/x/sys/windows"
)

// collectSystemMetrics reads CPU% and memory from kernel32 directly.
// Network throughput on Windows is left at zero for now — the iphlpapi
// MIB_IF_ROW2 layout is large and version-sensitive; reliable net stats
// would warrant a separate, well-tested implementation. CPU + RAM are
// enough for the dashboard's main signal.
func collectSystemMetrics() sysMetrics {
	m := sysMetrics{}

	if cpu, ok := readWindowsCPUPercent(); ok {
		m.CPUPercent = cpu
	}
	if memTotalMB, memUsedMB, memPct, ok := readWindowsMemory(); ok {
		m.MemTotalMB = memTotalMB
		m.MemUsedMB = memUsedMB
		m.MemPercent = memPct
	}
	// Net: zero (TODO).
	return m
}

// --- CPU ---------------------------------------------------------------

var (
	cpuOnce         sync.Once
	getSystemTimes  *syscall.LazyProc
	lastWinCPUIdle  uint64
	lastWinCPUTotal uint64
	lastWinCPUSet   bool
	winCPUMu        sync.Mutex
)

func loadCPUFns() {
	cpuOnce.Do(func() {
		k := syscall.NewLazyDLL("kernel32.dll")
		getSystemTimes = k.NewProc("GetSystemTimes")
	})
}

func filetimeToUint64(low, high uint32) uint64 {
	return uint64(high)<<32 | uint64(low)
}

// readWindowsCPUPercent samples GetSystemTimes twice and computes the busy
// ratio as 1 - dIdle / (dKernel + dUser). The first call returns 0% and
// stores the baseline; subsequent calls return the delta-based percentage.
func readWindowsCPUPercent() (float64, bool) {
	loadCPUFns()
	if getSystemTimes == nil {
		return 0, false
	}
	var idle, kernel, user windows.Filetime
	r1, _, _ := getSystemTimes.Call(
		uintptr(unsafe.Pointer(&idle)),
		uintptr(unsafe.Pointer(&kernel)),
		uintptr(unsafe.Pointer(&user)),
	)
	if r1 == 0 {
		return 0, false
	}
	idleT := filetimeToUint64(idle.LowDateTime, idle.HighDateTime)
	totalT := filetimeToUint64(kernel.LowDateTime, kernel.HighDateTime) +
		filetimeToUint64(user.LowDateTime, user.HighDateTime)
	winCPUMu.Lock()
	defer winCPUMu.Unlock()
	if !lastWinCPUSet {
		lastWinCPUIdle = idleT
		lastWinCPUTotal = totalT
		lastWinCPUSet = true
		return 0, true
	}
	dIdle := idleT - lastWinCPUIdle
	dTotal := totalT - lastWinCPUTotal
	lastWinCPUIdle = idleT
	lastWinCPUTotal = totalT
	if dTotal == 0 {
		return 0, true
	}
	pct := (1 - float64(dIdle)/float64(dTotal)) * 100
	if pct < 0 {
		pct = 0
	} else if pct > 100 {
		pct = 100
	}
	return pct, true
}

// --- Memory ------------------------------------------------------------

type memoryStatusEx struct {
	Length               uint32
	MemoryLoad           uint32
	TotalPhys            uint64
	AvailPhys            uint64
	TotalPageFile        uint64
	AvailPageFile        uint64
	TotalVirtual         uint64
	AvailVirtual         uint64
	AvailExtendedVirtual uint64
}

var globalMemoryStatusEx = syscall.NewLazyDLL("kernel32.dll").NewProc("GlobalMemoryStatusEx")

func readWindowsMemory() (totalMB, usedMB, percent float64, ok bool) {
	var ms memoryStatusEx
	ms.Length = uint32(unsafe.Sizeof(ms))
	r1, _, _ := globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&ms)))
	if r1 == 0 {
		return 0, 0, 0, false
	}
	const mb = 1024 * 1024
	totalMB = float64(ms.TotalPhys) / mb
	usedMB = float64(ms.TotalPhys-ms.AvailPhys) / mb
	if ms.TotalPhys > 0 {
		percent = float64(ms.TotalPhys-ms.AvailPhys) * 100 / float64(ms.TotalPhys)
	}
	return totalMB, usedMB, percent, true
}
