package system

import (
	"context"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/danielgtaylor/huma/v2"
)

// SystemInfoBody is the dashboard overview: OS identity plus live CPU, memory,
// disk, load, network, GPU, and temperature readings. Every section is best-effort —
// a source that's unavailable (e.g. no thermal zones in a VM) yields a zero
// value or empty list rather than failing the whole call.
type SystemInfoBody struct {
	OS                OSInfo         `json:"os" doc:"OS and kernel identity"`
	CPU               CPUInfo        `json:"cpu" doc:"Processor model and core count"`
	Memory            MemoryInfo     `json:"memory" doc:"RAM and swap usage in bytes"`
	Load              LoadInfo       `json:"load" doc:"Load averages (1/5/15 min)"`
	UptimeSec         int64          `json:"uptime_seconds" example:"12490" doc:"Seconds since boot"`
	BootTime          string         `json:"boot_time" example:"2026-06-19T12:08:00Z" doc:"Boot time (RFC3339, UTC)"`
	Disks             []DiskInfo     `json:"disks" doc:"Mounted block-device filesystems"`
	NetworkInterfaces []NetInterface `json:"network_interfaces" doc:"Network interfaces and their addresses"`
	Temperatures      []Temperature  `json:"temperatures" doc:"Thermal sensor readings in Celsius"`
	GPUs              []GPUInfo      `json:"gpus" doc:"Graphics processors detected via DRM sysfs"`
}

type GetInfoOutput struct{ Body SystemInfoBody }

func registerInfo(api huma.API, sampler *Sampler) {
	huma.Register(api, huma.Operation{
		OperationID: "system-get-info",
		Method:      "GET",
		Path:        "/api/system/info",
		Summary:     "Get system information",
		Description: "Returns an overview for a dashboard: OS/kernel identity, CPU, " +
			"memory and swap, mounted disks, load averages, uptime, network " +
			"interfaces, temperatures, and GPU information. All values come from cheap " +
			"local reads (/proc, /sys, syscalls) with no D-Bus dependency; each " +
			"section is best-effort.",
		Tags:     []string{tagSystem},
		Metadata: op("read"),
		Errors:   readErrors,
	}, func(ctx context.Context, _ *struct{}) (*GetInfoOutput, error) {
		uptime, boot := uptimeAndBoot()
		return &GetInfoOutput{Body: SystemInfoBody{
			OS:                osInfo(),
			CPU:               cpuInfo(),
			Memory:            memInfo(),
			Load:              loadInfo(sampler),
			UptimeSec:         uptime,
			BootTime:          boot.Format(time.RFC3339),
			Disks:             diskInfo(),
			NetworkInterfaces: netInfo(),
			Temperatures:      tempInfo(),
			GPUs:              gpuInfo(),
		}}, nil
	})
}

// uptimeAndBoot reads /proc/uptime (seconds since boot) and derives boot time.
// On any read error it returns zero values rather than failing the request.
func uptimeAndBoot() (int64, time.Time) {
	data, err := os.ReadFile("/proc/uptime")
	if err != nil {
		return 0, time.Time{}
	}
	fields := strings.Fields(string(data))
	if len(fields) == 0 {
		return 0, time.Time{}
	}
	secs, err := strconv.ParseFloat(fields[0], 64)
	if err != nil {
		return 0, time.Time{}
	}
	boot := time.Now().Add(-time.Duration(secs * float64(time.Second))).UTC()
	return int64(secs), boot
}
