package main

import (
	"context"
	"fmt"
	"os/exec"
	"time"

	"github.com/shirou/gopsutil/v3/disk"
	"github.com/shirou/gopsutil/v3/host"
	"github.com/shirou/gopsutil/v3/net"
)

type MetricsSnapshot struct {
	CollectedAt    time.Time
	Host           string
	Platform       string
	Uptime         string
	Procs          uint64
	Hardware       HardwareInfo
	HealthScore    int    // 0-100 system health score
	HealthScoreMsg string // Brief explanation

	CPU          CPUStatus
	GPU          []GPUStatus
	Memory       MemoryStatus
	Disks        []DiskStatus
	DiskIO       DiskIOStatus
	Network      []NetworkStatus
	Proxy        ProxyStatus
	Batteries    []BatteryStatus
	Thermal      ThermalStatus
	Sensors      []SensorReading
	Bluetooth    []BluetoothDevice
	TopProcesses []ProcessInfo
}

type HardwareInfo struct {
	Model     string // MacBook Pro 14-inch, 2021
	CPUModel  string // Apple M1 Pro / Intel Core i7
	TotalRAM  string // 16GB
	DiskSize  string // 512GB
	OSVersion string // macOS Sonoma 14.5
}

type DiskIOStatus struct {
	ReadRate  float64 // MB/s
	WriteRate float64 // MB/s
}

type ProcessInfo struct {
	Name   string
	CPU    float64
	Memory float64
}

type CPUStatus struct {
	Usage            float64
	PerCore          []float64
	PerCoreEstimated bool
	Load1            float64
	Load5            float64
	Load15           float64
	CoreCount        int
	LogicalCPU       int
	PCoreCount       int // Performance cores (Apple Silicon)
	ECoreCount       int // Efficiency cores (Apple Silicon)
}

type GPUStatus struct {
	Name        string
	Usage       float64
	MemoryUsed  float64
	MemoryTotal float64
	CoreCount   int
	Note        string
}

type MemoryStatus struct {
	Used        uint64
	Total       uint64
	UsedPercent float64
	SwapUsed    uint64
	SwapTotal   uint64
	Pressure    string // macOS memory pressure: normal/warn/critical
}

type DiskStatus struct {
	Mount       string
	Device      string
	Used        uint64
	Total       uint64
	UsedPercent float64
	Fstype      string
	External    bool
}

type NetworkStatus struct {
	Name      string
	RxRateMBs float64
	TxRateMBs float64
	IP        string
}

type ProxyStatus struct {
	Enabled bool
	Type    string // HTTP, SOCKS, System
	Host    string
}

type BatteryStatus struct {
	Percent    float64
	Status     string
	TimeLeft   string
	Health     string
	CycleCount int
}

type ThermalStatus struct {
	CPUTemp  float64
	GPUTemp  float64
	FanSpeed int
	FanCount int
}

type SensorReading struct {
	Label string
	Value float64
	Unit  string
	Note  string
}

type BluetoothDevice struct {
	Name      string
	Connected bool
	Battery   string
}

type Collector struct {
	prevNet    map[string]net.IOCountersStat
	lastNetAt  time.Time
	lastBTAt   time.Time
	lastBT     []BluetoothDevice
	lastGPUAt  time.Time
	cachedGPU  []GPUStatus
	prevDiskIO disk.IOCountersStat
	lastDiskAt time.Time
}

func NewCollector() *Collector {
	return &Collector{
		prevNet: make(map[string]net.IOCountersStat),
	}
}

func (c *Collector) Collect() (MetricsSnapshot, error) {
	now := time.Now()
	hostInfo, _ := host.Info()

	cpuStats, cpuErr := collectCPU()
	memStats, memErr := collectMemory()
	diskStats, diskErr := collectDisks()
	hwInfo := collectHardware(memStats.Total, diskStats)
	diskIO := c.collectDiskIO(now)
	netStats, netErr := c.collectNetwork(now)
	proxyStats := collectProxy()
	batteryStats, _ := collectBatteries()
	thermalStats := collectThermal()
	sensorStats, _ := collectSensors()
	gpuStats, gpuErr := c.collectGPU(now)
	btStats := c.collectBluetooth(now)
	topProcs := collectTopProcesses()

	var mergeErr error
	for _, e := range []error{cpuErr, memErr, diskErr, netErr, gpuErr} {
		if e != nil {
			if mergeErr == nil {
				mergeErr = e
			} else {
				mergeErr = fmt.Errorf("%v; %w", mergeErr, e)
			}
		}
	}

	// Calculate health score
	score, scoreMsg := calculateHealthScore(cpuStats, memStats, diskStats, diskIO, thermalStats)

	return MetricsSnapshot{
		CollectedAt:    now,
		Host:           hostInfo.Hostname,
		Platform:       fmt.Sprintf("%s %s", hostInfo.Platform, hostInfo.PlatformVersion),
		Uptime:         formatUptime(hostInfo.Uptime),
		Procs:          hostInfo.Procs,
		Hardware:       hwInfo,
		HealthScore:    score,
		HealthScoreMsg: scoreMsg,
		CPU:            cpuStats,
		GPU:            gpuStats,
		Memory:         memStats,
		Disks:          diskStats,
		DiskIO:         diskIO,
		Network:        netStats,
		Proxy:          proxyStats,
		Batteries:      batteryStats,
		Thermal:        thermalStats,
		Sensors:        sensorStats,
		Bluetooth:      btStats,
		TopProcesses:   topProcs,
	}, mergeErr
}

// Utility functions

func runCmd(ctx context.Context, name string, args ...string) (string, error) {
	cmd := exec.CommandContext(ctx, name, args...)
	output, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return string(output), nil
}

func commandExists(name string) bool {
	if name == "" {
		return false
	}
	defer func() {
		if r := recover(); r != nil {
			// If LookPath panics due to permissions or platform quirks, act as if the command is missing.
		}
	}()
	_, err := exec.LookPath(name)
	return err == nil
}

// humanBytes is defined in view.go to avoid duplication
