//go:build !windows

package client

import (
	"math"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
)

// collectResourcePressure gathers disk, CPU frequency/temp, and GPU metrics on Linux/macOS.
func collectResourcePressure() *ResourcePressure {
	r := &ResourcePressure{}

	// ── Disk free space ───────────────────────────────────────────────────────
	exe, _ := os.Executable()
	if exe == "" {
		exe = "/"
	}
	freeGB, totalGB, freePct := unixDiskStats(filepath.Dir(exe))
	if freePct >= 0 {
		r.DiskFreeGB = &freeGB
		r.DiskTotalGB = &totalGB
		r.DiskFreePct = &freePct
	}

	// ── CPU frequency ─────────────────────────────────────────────────────────
	cur := readSysInt("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq")
	max := readSysInt("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")
	if cur > 0 {
		mhz := cur / 1000
		r.CPUFreqMHz = &mhz
	}
	if max > 0 {
		mhz := max / 1000
		r.CPUMaxMHz = &mhz
		if cur > 0 {
			throttled := float64(cur) < float64(max)*0.80
			r.CPUThrottle = &throttled
		}
	}

	// ── CPU thermal ───────────────────────────────────────────────────────────
	if temp := probeCPUTemp(); temp > 0 {
		r.CPUTempC = &temp
	}

	// ── GPU (NVIDIA) ──────────────────────────────────────────────────────────
	r.GPUTempC, r.GPUUsagePct = probeGPU()

	return r
}

// unixDiskStats returns (freeGB, totalGB, freePct) for the filesystem at path.
func unixDiskStats(path string) (freeGB, totalGB float64, freePct int) {
	var stat syscall.Statfs_t
	if err := syscall.Statfs(path, &stat); err != nil {
		return 0, 0, -1
	}
	if stat.Blocks == 0 {
		return 0, 0, -1
	}
	bsize := uint64(stat.Bsize)
	total := stat.Blocks * bsize
	free := stat.Bavail * bsize

	const gb = 1024 * 1024 * 1024
	totalGB = math.Round(float64(total)/gb*100) / 100
	freeGB = math.Round(float64(free)/gb*100) / 100
	freePct = int(float64(free) / float64(total) * 100)
	return
}

// probeCPUTemp reads from the thermal subsystem. Returns 0 when unavailable.
func probeCPUTemp() int {
	// Try coretemp / k10temp style entries first (millidegrees)
	for _, zone := range []string{
		"/sys/class/thermal/thermal_zone0/temp",
		"/sys/class/thermal/thermal_zone1/temp",
		"/sys/class/hwmon/hwmon0/temp1_input",
		"/sys/class/hwmon/hwmon1/temp1_input",
	} {
		if n := readSysInt(zone); n > 0 {
			// values > 1000 are in millidegrees
			if n > 1000 {
				return n / 1000
			}
			return n
		}
	}
	return 0
}

// readSysInt reads a single integer from a sysfs/procfs file.
func readSysInt(path string) int {
	b, err := os.ReadFile(path)
	if err != nil {
		return 0
	}
	n, err := strconv.Atoi(strings.TrimSpace(string(b)))
	if err != nil {
		return 0
	}
	return n
}
