//go:build testing

package records_test

import (
	"sort"
	"testing"

	"github.com/henrygd/beszel/internal/entities/container"
	"github.com/henrygd/beszel/internal/entities/system"
	"github.com/henrygd/beszel/internal/records"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestAverageSystemStatsSlice_Empty(t *testing.T) {
	result := records.AverageSystemStatsSlice(nil)
	assert.Equal(t, system.Stats{}, result)

	result = records.AverageSystemStatsSlice([]system.Stats{})
	assert.Equal(t, system.Stats{}, result)
}

func TestAverageSystemStatsSlice_SingleRecord(t *testing.T) {
	input := []system.Stats{
		{
			Cpu:          45.67,
			Mem:          16.0,
			MemUsed:      8.5,
			MemPct:       53.12,
			MemBuffCache: 2.0,
			Swap:         4.0,
			SwapUsed:     1.0,
			DiskTotal:    500.0,
			DiskUsed:     250.0,
			DiskPct:      50.0,
			DiskReadPs:   100.5,
			DiskWritePs:  200.75,
			NetworkSent:  10.5,
			NetworkRecv:  20.25,
			LoadAvg:      [3]float64{1.5, 2.0, 3.5},
			Bandwidth:    [2]uint64{1000, 2000},
			DiskIO:       [2]uint64{500, 600},
			Battery:      [2]uint8{80, 1},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	assert.Equal(t, 45.67, result.Cpu)
	assert.Equal(t, 16.0, result.Mem)
	assert.Equal(t, 8.5, result.MemUsed)
	assert.Equal(t, 53.12, result.MemPct)
	assert.Equal(t, 2.0, result.MemBuffCache)
	assert.Equal(t, 4.0, result.Swap)
	assert.Equal(t, 1.0, result.SwapUsed)
	assert.Equal(t, 500.0, result.DiskTotal)
	assert.Equal(t, 250.0, result.DiskUsed)
	assert.Equal(t, 50.0, result.DiskPct)
	assert.Equal(t, 100.5, result.DiskReadPs)
	assert.Equal(t, 200.75, result.DiskWritePs)
	assert.Equal(t, 10.5, result.NetworkSent)
	assert.Equal(t, 20.25, result.NetworkRecv)
	assert.Equal(t, [3]float64{1.5, 2.0, 3.5}, result.LoadAvg)
	assert.Equal(t, [2]uint64{1000, 2000}, result.Bandwidth)
	assert.Equal(t, [2]uint64{500, 600}, result.DiskIO)
	assert.Equal(t, uint8(80), result.Battery[0])
	assert.Equal(t, uint8(1), result.Battery[1])
}

func TestAverageSystemStatsSlice_BasicAveraging(t *testing.T) {
	input := []system.Stats{
		{
			Cpu:          20.0,
			Mem:          16.0,
			MemUsed:      6.0,
			MemPct:       37.5,
			MemBuffCache: 1.0,
			MemZfsArc:    0.5,
			Swap:         4.0,
			SwapUsed:     1.0,
			DiskTotal:    500.0,
			DiskUsed:     200.0,
			DiskPct:      40.0,
			DiskReadPs:   100.0,
			DiskWritePs:  200.0,
			NetworkSent:  10.0,
			NetworkRecv:  20.0,
			LoadAvg:      [3]float64{1.0, 2.0, 3.0},
			Bandwidth:    [2]uint64{1000, 2000},
			DiskIO:       [2]uint64{400, 600},
			Battery:      [2]uint8{80, 1},
		},
		{
			Cpu:          40.0,
			Mem:          16.0,
			MemUsed:      10.0,
			MemPct:       62.5,
			MemBuffCache: 3.0,
			MemZfsArc:    1.5,
			Swap:         4.0,
			SwapUsed:     3.0,
			DiskTotal:    500.0,
			DiskUsed:     300.0,
			DiskPct:      60.0,
			DiskReadPs:   200.0,
			DiskWritePs:  400.0,
			NetworkSent:  30.0,
			NetworkRecv:  40.0,
			LoadAvg:      [3]float64{3.0, 4.0, 5.0},
			Bandwidth:    [2]uint64{3000, 4000},
			DiskIO:       [2]uint64{600, 800},
			Battery:      [2]uint8{60, 1},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	assert.Equal(t, 30.0, result.Cpu)
	assert.Equal(t, 16.0, result.Mem)
	assert.Equal(t, 8.0, result.MemUsed)
	assert.Equal(t, 50.0, result.MemPct)
	assert.Equal(t, 2.0, result.MemBuffCache)
	assert.Equal(t, 1.0, result.MemZfsArc)
	assert.Equal(t, 4.0, result.Swap)
	assert.Equal(t, 2.0, result.SwapUsed)
	assert.Equal(t, 500.0, result.DiskTotal)
	assert.Equal(t, 250.0, result.DiskUsed)
	assert.Equal(t, 50.0, result.DiskPct)
	assert.Equal(t, 150.0, result.DiskReadPs)
	assert.Equal(t, 300.0, result.DiskWritePs)
	assert.Equal(t, 20.0, result.NetworkSent)
	assert.Equal(t, 30.0, result.NetworkRecv)
	assert.Equal(t, [3]float64{2.0, 3.0, 4.0}, result.LoadAvg)
	assert.Equal(t, [2]uint64{2000, 3000}, result.Bandwidth)
	assert.Equal(t, [2]uint64{500, 700}, result.DiskIO)
	assert.Equal(t, uint8(70), result.Battery[0])
	assert.Equal(t, uint8(1), result.Battery[1])
}

func TestAverageSystemStatsSlice_PeakValues(t *testing.T) {
	input := []system.Stats{
		{
			Cpu:            20.0,
			MaxCpu:         25.0,
			MemUsed:        6.0,
			MaxMem:         7.0,
			NetworkSent:    10.0,
			MaxNetworkSent: 15.0,
			NetworkRecv:    20.0,
			MaxNetworkRecv: 25.0,
			DiskReadPs:     100.0,
			MaxDiskReadPs:  120.0,
			DiskWritePs:    200.0,
			MaxDiskWritePs: 220.0,
			Bandwidth:      [2]uint64{1000, 2000},
			MaxBandwidth:   [2]uint64{1500, 2500},
			DiskIO:         [2]uint64{400, 600},
			MaxDiskIO:      [2]uint64{500, 700},
			DiskIoStats:    [6]float64{10.0, 20.0, 30.0, 5.0, 8.0, 12.0},
			MaxDiskIoStats: [6]float64{15.0, 25.0, 35.0, 6.0, 9.0, 14.0},
		},
		{
			Cpu:            40.0,
			MaxCpu:         50.0,
			MemUsed:        10.0,
			MaxMem:         12.0,
			NetworkSent:    30.0,
			MaxNetworkSent: 35.0,
			NetworkRecv:    40.0,
			MaxNetworkRecv: 45.0,
			DiskReadPs:     200.0,
			MaxDiskReadPs:  210.0,
			DiskWritePs:    400.0,
			MaxDiskWritePs: 410.0,
			Bandwidth:      [2]uint64{3000, 4000},
			MaxBandwidth:   [2]uint64{3500, 4500},
			DiskIO:         [2]uint64{600, 800},
			MaxDiskIO:      [2]uint64{650, 850},
			DiskIoStats:    [6]float64{50.0, 60.0, 70.0, 15.0, 18.0, 22.0},
			MaxDiskIoStats: [6]float64{55.0, 65.0, 75.0, 16.0, 19.0, 23.0},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	assert.Equal(t, 50.0, result.MaxCpu)
	assert.Equal(t, 12.0, result.MaxMem)
	assert.Equal(t, 35.0, result.MaxNetworkSent)
	assert.Equal(t, 45.0, result.MaxNetworkRecv)
	assert.Equal(t, 210.0, result.MaxDiskReadPs)
	assert.Equal(t, 410.0, result.MaxDiskWritePs)
	assert.Equal(t, [2]uint64{3500, 4500}, result.MaxBandwidth)
	assert.Equal(t, [2]uint64{650, 850}, result.MaxDiskIO)
	assert.Equal(t, [6]float64{30.0, 40.0, 50.0, 10.0, 13.0, 17.0}, result.DiskIoStats)
	assert.Equal(t, [6]float64{55.0, 65.0, 75.0, 16.0, 19.0, 23.0}, result.MaxDiskIoStats)
}

func TestAverageSystemStatsSlice_DiskIoStats(t *testing.T) {
	input := []system.Stats{
		{
			Cpu:            10.0,
			DiskIoStats:    [6]float64{10.0, 20.0, 30.0, 5.0, 8.0, 12.0},
			MaxDiskIoStats: [6]float64{12.0, 22.0, 32.0, 6.0, 9.0, 13.0},
		},
		{
			Cpu:            20.0,
			DiskIoStats:    [6]float64{30.0, 40.0, 50.0, 15.0, 18.0, 22.0},
			MaxDiskIoStats: [6]float64{28.0, 38.0, 48.0, 14.0, 17.0, 21.0},
		},
		{
			Cpu:            30.0,
			DiskIoStats:    [6]float64{20.0, 30.0, 40.0, 10.0, 12.0, 16.0},
			MaxDiskIoStats: [6]float64{25.0, 35.0, 45.0, 11.0, 13.0, 17.0},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	// Average: (10+30+20)/3=20, (20+40+30)/3=30, (30+50+40)/3=40, (5+15+10)/3=10, (8+18+12)/3≈12.67, (12+22+16)/3≈16.67
	assert.Equal(t, 20.0, result.DiskIoStats[0])
	assert.Equal(t, 30.0, result.DiskIoStats[1])
	assert.Equal(t, 40.0, result.DiskIoStats[2])
	assert.Equal(t, 10.0, result.DiskIoStats[3])
	assert.Equal(t, 12.67, result.DiskIoStats[4])
	assert.Equal(t, 16.67, result.DiskIoStats[5])
	// Max: current DiskIoStats[0] wins for record 2 (30 > MaxDiskIoStats 28)
	assert.Equal(t, 30.0, result.MaxDiskIoStats[0])
	assert.Equal(t, 40.0, result.MaxDiskIoStats[1])
	assert.Equal(t, 50.0, result.MaxDiskIoStats[2])
	assert.Equal(t, 15.0, result.MaxDiskIoStats[3])
	assert.Equal(t, 18.0, result.MaxDiskIoStats[4])
	assert.Equal(t, 22.0, result.MaxDiskIoStats[5])
}

// Tests that current DiskIoStats values are considered when computing MaxDiskIoStats.
func TestAverageSystemStatsSlice_DiskIoStatsPeakFromCurrentValues(t *testing.T) {
	input := []system.Stats{
		{Cpu: 10.0, DiskIoStats: [6]float64{95.0, 90.0, 85.0, 50.0, 60.0, 80.0}, MaxDiskIoStats: [6]float64{80.0, 80.0, 80.0, 40.0, 50.0, 70.0}},
		{Cpu: 20.0, DiskIoStats: [6]float64{10.0, 10.0, 10.0, 5.0, 6.0, 8.0}, MaxDiskIoStats: [6]float64{20.0, 20.0, 20.0, 10.0, 12.0, 16.0}},
	}

	result := records.AverageSystemStatsSlice(input)

	// Current value from first record (95, 90, 85, 50, 60, 80) beats MaxDiskIoStats in both records
	assert.Equal(t, 95.0, result.MaxDiskIoStats[0])
	assert.Equal(t, 90.0, result.MaxDiskIoStats[1])
	assert.Equal(t, 85.0, result.MaxDiskIoStats[2])
	assert.Equal(t, 50.0, result.MaxDiskIoStats[3])
	assert.Equal(t, 60.0, result.MaxDiskIoStats[4])
	assert.Equal(t, 80.0, result.MaxDiskIoStats[5])
}

// Tests that current values are considered when computing peaks
// (i.e., current cpu > MaxCpu should still win).
func TestAverageSystemStatsSlice_PeakFromCurrentValues(t *testing.T) {
	input := []system.Stats{
		{Cpu: 95.0, MaxCpu: 80.0, MemUsed: 15.0, MaxMem: 10.0},
		{Cpu: 10.0, MaxCpu: 20.0, MemUsed: 5.0, MaxMem: 8.0},
	}

	result := records.AverageSystemStatsSlice(input)

	assert.Equal(t, 95.0, result.MaxCpu)
	assert.Equal(t, 15.0, result.MaxMem)
}

// Tests that records without temperature data are excluded from the temperature average.
func TestAverageSystemStatsSlice_Temperatures(t *testing.T) {
	input := []system.Stats{
		{
			Cpu:          10.0,
			Temperatures: map[string]float64{"cpu": 60.0, "gpu": 70.0},
		},
		{
			Cpu:          20.0,
			Temperatures: map[string]float64{"cpu": 80.0, "gpu": 90.0},
		},
		{
			// No temperatures - should not affect temp averaging
			Cpu: 30.0,
		},
	}

	result := records.AverageSystemStatsSlice(input)

	require.NotNil(t, result.Temperatures)
	// Average over 2 records that had temps, not 3
	assert.Equal(t, 70.0, result.Temperatures["cpu"])
	assert.Equal(t, 80.0, result.Temperatures["gpu"])
}

// Tests that fan speeds are averaged and records without fan data are excluded.
func TestAverageSystemStatsSlice_Fans(t *testing.T) {
	input := []system.Stats{
		{
			Fans: map[string]uint16{"cpu": 60_000, "case": 1_000},
		},
		{
			Fans: map[string]uint16{"cpu": 50_000, "case": 2_000},
		},
		{
			// No fan data - should not affect fan averaging
			Cpu: 30.0,
		},
	}

	result := records.AverageSystemStatsSlice(input)

	require.NotNil(t, result.Fans)
	assert.Equal(t, uint16(55_000), result.Fans["cpu"])
	assert.Equal(t, uint16(1_500), result.Fans["case"])
}

func TestAverageSystemStatsSlice_NetworkInterfaces(t *testing.T) {
	input := []system.Stats{
		{
			Cpu: 10.0,
			NetworkInterfaces: map[string][4]uint64{
				"eth0": {100, 200, 150, 250},
				"eth1": {50, 60, 70, 80},
			},
		},
		{
			Cpu: 20.0,
			NetworkInterfaces: map[string][4]uint64{
				"eth0": {200, 400, 300, 500},
				"eth1": {150, 160, 170, 180},
			},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	require.NotNil(t, result.NetworkInterfaces)
	// [0] and [1] are averaged, [2] and [3] are max
	assert.Equal(t, [4]uint64{150, 300, 300, 500}, result.NetworkInterfaces["eth0"])
	assert.Equal(t, [4]uint64{100, 110, 170, 180}, result.NetworkInterfaces["eth1"])
}

func TestAverageSystemStatsSlice_ExtraFs(t *testing.T) {
	input := []system.Stats{
		{
			Cpu: 10.0,
			ExtraFs: map[string]*system.FsStats{
				"/data": {
					DiskTotal:         1000.0,
					DiskUsed:          400.0,
					DiskReadPs:        50.0,
					DiskWritePs:       100.0,
					MaxDiskReadPS:     60.0,
					MaxDiskWritePS:    110.0,
					DiskReadBytes:     5000,
					DiskWriteBytes:    10000,
					MaxDiskReadBytes:  6000,
					MaxDiskWriteBytes: 11000,
					DiskIoStats:       [6]float64{10.0, 20.0, 30.0, 5.0, 8.0, 12.0},
					MaxDiskIoStats:    [6]float64{12.0, 22.0, 32.0, 6.0, 9.0, 13.0},
				},
			},
		},
		{
			Cpu: 20.0,
			ExtraFs: map[string]*system.FsStats{
				"/data": {
					DiskTotal:         1000.0,
					DiskUsed:          600.0,
					DiskReadPs:        150.0,
					DiskWritePs:       200.0,
					MaxDiskReadPS:     160.0,
					MaxDiskWritePS:    210.0,
					DiskReadBytes:     15000,
					DiskWriteBytes:    20000,
					MaxDiskReadBytes:  16000,
					MaxDiskWriteBytes: 21000,
					DiskIoStats:       [6]float64{50.0, 60.0, 70.0, 15.0, 18.0, 22.0},
					MaxDiskIoStats:    [6]float64{55.0, 65.0, 75.0, 16.0, 19.0, 23.0},
				},
			},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	require.NotNil(t, result.ExtraFs)
	require.NotNil(t, result.ExtraFs["/data"])
	fs := result.ExtraFs["/data"]
	assert.Equal(t, 1000.0, fs.DiskTotal)
	assert.Equal(t, 500.0, fs.DiskUsed)
	assert.Equal(t, 100.0, fs.DiskReadPs)
	assert.Equal(t, 150.0, fs.DiskWritePs)
	assert.Equal(t, 160.0, fs.MaxDiskReadPS)
	assert.Equal(t, 210.0, fs.MaxDiskWritePS)
	assert.Equal(t, uint64(10000), fs.DiskReadBytes)
	assert.Equal(t, uint64(15000), fs.DiskWriteBytes)
	assert.Equal(t, uint64(16000), fs.MaxDiskReadBytes)
	assert.Equal(t, uint64(21000), fs.MaxDiskWriteBytes)
	assert.Equal(t, [6]float64{30.0, 40.0, 50.0, 10.0, 13.0, 17.0}, fs.DiskIoStats)
	assert.Equal(t, [6]float64{55.0, 65.0, 75.0, 16.0, 19.0, 23.0}, fs.MaxDiskIoStats)
}

// Tests that ExtraFs DiskIoStats peak considers current values, not just previous peaks.
func TestAverageSystemStatsSlice_ExtraFsDiskIoStatsPeakFromCurrentValues(t *testing.T) {
	input := []system.Stats{
		{
			Cpu: 10.0,
			ExtraFs: map[string]*system.FsStats{
				"/data": {
					DiskIoStats:    [6]float64{95.0, 90.0, 85.0, 50.0, 60.0, 80.0}, // exceeds MaxDiskIoStats
					MaxDiskIoStats: [6]float64{80.0, 80.0, 80.0, 40.0, 50.0, 70.0},
				},
			},
		},
		{
			Cpu: 20.0,
			ExtraFs: map[string]*system.FsStats{
				"/data": {
					DiskIoStats:    [6]float64{10.0, 10.0, 10.0, 5.0, 6.0, 8.0},
					MaxDiskIoStats: [6]float64{20.0, 20.0, 20.0, 10.0, 12.0, 16.0},
				},
			},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	fs := result.ExtraFs["/data"]
	assert.Equal(t, 95.0, fs.MaxDiskIoStats[0])
	assert.Equal(t, 90.0, fs.MaxDiskIoStats[1])
	assert.Equal(t, 85.0, fs.MaxDiskIoStats[2])
	assert.Equal(t, 50.0, fs.MaxDiskIoStats[3])
	assert.Equal(t, 60.0, fs.MaxDiskIoStats[4])
	assert.Equal(t, 80.0, fs.MaxDiskIoStats[5])
}

// Tests that extra FS peak values consider current values, not just previous peaks.
func TestAverageSystemStatsSlice_ExtraFsPeaksFromCurrentValues(t *testing.T) {
	input := []system.Stats{
		{
			Cpu: 10.0,
			ExtraFs: map[string]*system.FsStats{
				"/data": {
					DiskReadPs:       500.0, // exceeds MaxDiskReadPS
					MaxDiskReadPS:    100.0,
					DiskReadBytes:    50000,
					MaxDiskReadBytes: 10000,
				},
			},
		},
		{
			Cpu: 20.0,
			ExtraFs: map[string]*system.FsStats{
				"/data": {
					DiskReadPs:       50.0,
					MaxDiskReadPS:    200.0,
					DiskReadBytes:    5000,
					MaxDiskReadBytes: 20000,
				},
			},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	fs := result.ExtraFs["/data"]
	assert.Equal(t, 500.0, fs.MaxDiskReadPS)
	assert.Equal(t, uint64(50000), fs.MaxDiskReadBytes)
}

func TestAverageSystemStatsSlice_GPUData(t *testing.T) {
	input := []system.Stats{
		{
			Cpu: 10.0,
			GPUData: map[string]system.GPUData{
				"gpu0": {
					Name:        "RTX 4090",
					Temperature: 60.0,
					MemoryUsed:  4.0,
					MemoryTotal: 24.0,
					Usage:       30.0,
					Power:       200.0,
					Count:       1.0,
					Engines: map[string]float64{
						"3D":    50.0,
						"Video": 10.0,
					},
				},
			},
		},
		{
			Cpu: 20.0,
			GPUData: map[string]system.GPUData{
				"gpu0": {
					Name:        "RTX 4090",
					Temperature: 80.0,
					MemoryUsed:  8.0,
					MemoryTotal: 24.0,
					Usage:       70.0,
					Power:       300.0,
					Count:       1.0,
					Engines: map[string]float64{
						"3D":    90.0,
						"Video": 30.0,
					},
				},
			},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	require.NotNil(t, result.GPUData)
	gpu := result.GPUData["gpu0"]
	assert.Equal(t, "RTX 4090", gpu.Name)
	assert.Equal(t, 70.0, gpu.Temperature)
	assert.Equal(t, 6.0, gpu.MemoryUsed)
	assert.Equal(t, 24.0, gpu.MemoryTotal)
	assert.Equal(t, 50.0, gpu.Usage)
	assert.Equal(t, 250.0, gpu.Power)
	assert.Equal(t, 1.0, gpu.Count)
	require.NotNil(t, gpu.Engines)
	assert.Equal(t, 70.0, gpu.Engines["3D"])
	assert.Equal(t, 20.0, gpu.Engines["Video"])
}

func TestAverageSystemStatsSlice_MultipleGPUs(t *testing.T) {
	input := []system.Stats{
		{
			Cpu: 10.0,
			GPUData: map[string]system.GPUData{
				"gpu0": {Name: "GPU A", Usage: 20.0, Temperature: 50.0},
				"gpu1": {Name: "GPU B", Usage: 60.0, Temperature: 70.0},
			},
		},
		{
			Cpu: 20.0,
			GPUData: map[string]system.GPUData{
				"gpu0": {Name: "GPU A", Usage: 40.0, Temperature: 60.0},
				"gpu1": {Name: "GPU B", Usage: 80.0, Temperature: 80.0},
			},
		},
	}

	result := records.AverageSystemStatsSlice(input)

	require.NotNil(t, result.GPUData)
	assert.Equal(t, 30.0, result.GPUData["gpu0"].Usage)
	assert.Equal(t, 55.0, result.GPUData["gpu0"].Temperature)
	assert.Equal(t, 70.0, result.GPUData["gpu1"].Usage)
	assert.Equal(t, 75.0, result.GPUData["gpu1"].Temperature)
}

func TestAverageSystemStatsSlice_CpuCoresUsage(t *testing.T) {
	input := []system.Stats{
		{Cpu: 10.0, CpuCoresUsage: system.Uint8Slice{10, 20, 30, 40}},
		{Cpu: 20.0, CpuCoresUsage: system.Uint8Slice{30, 40, 50, 60}},
	}

	result := records.AverageSystemStatsSlice(input)

	require.NotNil(t, result.CpuCoresUsage)
	assert.Equal(t, system.Uint8Slice{20, 30, 40, 50}, result.CpuCoresUsage)
}

// Tests that per-core usage rounds correctly (e.g., 15.5 -> 16 via math.Round).
func TestAverageSystemStatsSlice_CpuCoresUsageRounding(t *testing.T) {
	input := []system.Stats{
		{Cpu: 10.0, CpuCoresUsage: system.Uint8Slice{11}},
		{Cpu: 20.0, CpuCoresUsage: system.Uint8Slice{20}},
	}

	result := records.AverageSystemStatsSlice(input)

	require.NotNil(t, result.CpuCoresUsage)
	// (11+20)/2 = 15.5, rounds to 16
	assert.Equal(t, uint8(16), result.CpuCoresUsage[0])
}

func TestAverageSystemStatsSlice_CpuBreakdown(t *testing.T) {
	input := []system.Stats{
		{Cpu: 10.0, CpuBreakdown: []float64{5.0, 3.0, 1.0, 0.5, 90.5}},
		{Cpu: 20.0, CpuBreakdown: []float64{15.0, 7.0, 3.0, 1.5, 73.5}},
	}

	result := records.AverageSystemStatsSlice(input)

	require.NotNil(t, result.CpuBreakdown)
	assert.Equal(t, []float64{10.0, 5.0, 2.0, 1.0, 82.0}, result.CpuBreakdown)
}

// Tests that Battery[1] (charge state) uses the last record's value.
func TestAverageSystemStatsSlice_BatteryLastChargeState(t *testing.T) {
	input := []system.Stats{
		{Cpu: 10.0, Battery: [2]uint8{100, 1}}, // charging
		{Cpu: 20.0, Battery: [2]uint8{90, 0}},  // not charging
	}

	result := records.AverageSystemStatsSlice(input)

	assert.Equal(t, uint8(95), result.Battery[0])
	assert.Equal(t, uint8(0), result.Battery[1]) // last record's charge state
}

func TestAverageSystemStatsSlice_BatteriesIndependentSamples(t *testing.T) {
	input := []system.Stats{
		{Battery: [2]uint8{80, 4}, Batteries: map[string]uint8{"Primary": 80, "Mouse": 0}},
		{Battery: [2]uint8{60, 3}, Batteries: map[string]uint8{"Primary": 60}},
		{Battery: [2]uint8{30, 4}, Batteries: map[string]uint8{"Mouse": 40}},
		{},
	}
	result := records.AverageSystemStatsSlice(input)
	assert.Equal(t, map[string]uint8{"Primary": 70, "Mouse": 20}, result.Batteries)
	assert.Equal(t, uint8(56), result.Battery[0], "representative battery excludes absent samples")
	assert.Equal(t, uint8(4), result.Battery[1], "representative state comes from its latest sample")
}

func TestAverageSystemStatsSlice_ZeroRepresentativeBattery(t *testing.T) {
	result := records.AverageSystemStatsSlice([]system.Stats{
		{Battery: [2]uint8{0, 1}, Batteries: map[string]uint8{"Primary": 0}},
		{},
	})
	assert.Equal(t, [2]uint8{0, 1}, result.Battery)
	assert.Equal(t, map[string]uint8{"Primary": 0}, result.Batteries)
}

func TestAverageSystemStatsSlice_ThreeRecordsRounding(t *testing.T) {
	input := []system.Stats{
		{Cpu: 10.0, Mem: 8.0},
		{Cpu: 20.0, Mem: 8.0},
		{Cpu: 30.0, Mem: 8.0},
	}

	result := records.AverageSystemStatsSlice(input)

	assert.Equal(t, 20.0, result.Cpu)
	assert.Equal(t, 8.0, result.Mem)
}

// Tests records where some have optional fields and others don't.
func TestAverageSystemStatsSlice_MixedOptionalFields(t *testing.T) {
	input := []system.Stats{
		{
			Cpu:           10.0,
			CpuCoresUsage: system.Uint8Slice{50, 60},
			CpuBreakdown:  []float64{5.0, 3.0, 1.0, 0.5, 90.5},
			GPUData: map[string]system.GPUData{
				"gpu0": {Name: "GPU", Usage: 40.0},
			},
		},
		{
			Cpu: 20.0,
			// No CpuCoresUsage, CpuBreakdown, or GPUData
		},
	}

	result := records.AverageSystemStatsSlice(input)

	assert.Equal(t, 15.0, result.Cpu)
	// CpuCoresUsage: only 1 record had it, so sum/2
	require.NotNil(t, result.CpuCoresUsage)
	assert.Equal(t, uint8(25), result.CpuCoresUsage[0])
	assert.Equal(t, uint8(30), result.CpuCoresUsage[1])
	// CpuBreakdown: only 1 record had it, so sum/2
	require.NotNil(t, result.CpuBreakdown)
	assert.Equal(t, 2.5, result.CpuBreakdown[0])
	// GPUData: only 1 record had it, so sum/2
	require.NotNil(t, result.GPUData)
	assert.Equal(t, 20.0, result.GPUData["gpu0"].Usage)
}

// Tests with 10 records matching the common real-world case (10 x 1m -> 1 x 10m).
func TestAverageSystemStatsSlice_TenRecords(t *testing.T) {
	input := make([]system.Stats, 10)
	for i := range input {
		input[i] = system.Stats{
			Cpu:         float64(i * 10), // 0, 10, 20, ..., 90
			Mem:         16.0,
			MemUsed:     float64(4 + i),  // 4, 5, 6, ..., 13
			MemPct:      float64(25 + i), // 25, 26, ..., 34
			DiskTotal:   500.0,
			DiskUsed:    250.0,
			DiskPct:     50.0,
			NetworkSent: float64(i),
			NetworkRecv: float64(i * 2),
			Bandwidth:   [2]uint64{uint64(i * 1000), uint64(i * 2000)},
			LoadAvg:     [3]float64{float64(i), float64(i) * 0.5, float64(i) * 0.25},
		}
	}

	result := records.AverageSystemStatsSlice(input)

	assert.Equal(t, 45.0, result.Cpu)    // avg of 0..90
	assert.Equal(t, 16.0, result.Mem)    // constant
	assert.Equal(t, 8.5, result.MemUsed) // avg of 4..13
	assert.Equal(t, 29.5, result.MemPct) // avg of 25..34
	assert.Equal(t, 500.0, result.DiskTotal)
	assert.Equal(t, 250.0, result.DiskUsed)
	assert.Equal(t, 50.0, result.DiskPct)
	assert.Equal(t, 4.5, result.NetworkSent)
	assert.Equal(t, 9.0, result.NetworkRecv)
	assert.Equal(t, [2]uint64{4500, 9000}, result.Bandwidth)
}

// --- Container Stats Tests ---

func TestAverageContainerStatsSlice_Empty(t *testing.T) {
	result := records.AverageContainerStatsSlice(nil)
	assert.Equal(t, []container.Stats{}, result)

	result = records.AverageContainerStatsSlice([][]container.Stats{})
	assert.Equal(t, []container.Stats{}, result)
}

func TestAverageContainerStatsSlice_SingleRecord(t *testing.T) {
	input := [][]container.Stats{
		{
			{Name: "nginx", Cpu: 5.0, Mem: 128.0, Bandwidth: [2]uint64{1000, 2000}},
		},
	}

	result := records.AverageContainerStatsSlice(input)

	require.Len(t, result, 1)
	assert.Equal(t, "nginx", result[0].Name)
	assert.Equal(t, 5.0, result[0].Cpu)
	assert.Equal(t, 128.0, result[0].Mem)
	assert.Equal(t, [2]uint64{1000, 2000}, result[0].Bandwidth)
}

func TestAverageContainerStatsSlice_BasicAveraging(t *testing.T) {
	input := [][]container.Stats{
		{
			{Name: "nginx", Cpu: 10.0, Mem: 100.0, Bandwidth: [2]uint64{1000, 2000}},
			{Name: "redis", Cpu: 5.0, Mem: 64.0, Bandwidth: [2]uint64{500, 1000}},
		},
		{
			{Name: "nginx", Cpu: 20.0, Mem: 200.0, Bandwidth: [2]uint64{3000, 4000}},
			{Name: "redis", Cpu: 15.0, Mem: 128.0, Bandwidth: [2]uint64{1500, 2000}},
		},
	}

	result := records.AverageContainerStatsSlice(input)
	sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name })

	require.Len(t, result, 2)

	assert.Equal(t, "nginx", result[0].Name)
	assert.Equal(t, 15.0, result[0].Cpu)
	assert.Equal(t, 150.0, result[0].Mem)
	assert.Equal(t, [2]uint64{2000, 3000}, result[0].Bandwidth)

	assert.Equal(t, "redis", result[1].Name)
	assert.Equal(t, 10.0, result[1].Cpu)
	assert.Equal(t, 96.0, result[1].Mem)
	assert.Equal(t, [2]uint64{1000, 1500}, result[1].Bandwidth)
}

// Tests containers that appear in some records but not all.
func TestAverageContainerStatsSlice_ContainerAppearsInSomeRecords(t *testing.T) {
	input := [][]container.Stats{
		{
			{Name: "nginx", Cpu: 10.0, Mem: 100.0},
			{Name: "redis", Cpu: 5.0, Mem: 64.0},
		},
		{
			{Name: "nginx", Cpu: 20.0, Mem: 200.0},
			// redis not present
		},
	}

	result := records.AverageContainerStatsSlice(input)
	sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name })

	require.Len(t, result, 2)

	assert.Equal(t, "nginx", result[0].Name)
	assert.Equal(t, 15.0, result[0].Cpu)
	assert.Equal(t, 150.0, result[0].Mem)

	// redis: sum / count where count = total records (2), not records containing redis
	assert.Equal(t, "redis", result[1].Name)
	assert.Equal(t, 2.5, result[1].Cpu)
	assert.Equal(t, 32.0, result[1].Mem)
}

// Tests backward compatibility with deprecated NetworkSent/NetworkRecv (MB) when Bandwidth is zero.
func TestAverageContainerStatsSlice_DeprecatedNetworkFields(t *testing.T) {
	input := [][]container.Stats{
		{
			{Name: "nginx", Cpu: 10.0, Mem: 100.0, NetworkSent: 1.0, NetworkRecv: 2.0}, // 1 MB, 2 MB
		},
		{
			{Name: "nginx", Cpu: 20.0, Mem: 200.0, NetworkSent: 3.0, NetworkRecv: 4.0}, // 3 MB, 4 MB
		},
	}

	result := records.AverageContainerStatsSlice(input)

	require.Len(t, result, 1)
	assert.Equal(t, "nginx", result[0].Name)
	// avg sent = (1*1048576 + 3*1048576) / 2 = 2*1048576
	assert.Equal(t, uint64(2*1048576), result[0].Bandwidth[0])
	// avg recv = (2*1048576 + 4*1048576) / 2 = 3*1048576
	assert.Equal(t, uint64(3*1048576), result[0].Bandwidth[1])
}

// Tests that when Bandwidth is set, deprecated NetworkSent/NetworkRecv are ignored.
func TestAverageContainerStatsSlice_MixedBandwidthAndDeprecated(t *testing.T) {
	input := [][]container.Stats{
		{
			{Name: "nginx", Cpu: 10.0, Mem: 100.0, Bandwidth: [2]uint64{5000, 6000}, NetworkSent: 99.0, NetworkRecv: 99.0},
		},
		{
			{Name: "nginx", Cpu: 20.0, Mem: 200.0, Bandwidth: [2]uint64{7000, 8000}},
		},
	}

	result := records.AverageContainerStatsSlice(input)

	require.Len(t, result, 1)
	assert.Equal(t, uint64(6000), result[0].Bandwidth[0])
	assert.Equal(t, uint64(7000), result[0].Bandwidth[1])
}

func TestAverageContainerStatsSlice_ThreeRecords(t *testing.T) {
	input := [][]container.Stats{
		{{Name: "app", Cpu: 1.0, Mem: 100.0}},
		{{Name: "app", Cpu: 2.0, Mem: 200.0}},
		{{Name: "app", Cpu: 3.0, Mem: 300.0}},
	}

	result := records.AverageContainerStatsSlice(input)

	require.Len(t, result, 1)
	assert.Equal(t, 2.0, result[0].Cpu)
	assert.Equal(t, 200.0, result[0].Mem)
}

func TestAverageContainerStatsSlice_ManyContainers(t *testing.T) {
	input := [][]container.Stats{
		{
			{Name: "a", Cpu: 10.0, Mem: 100.0},
			{Name: "b", Cpu: 20.0, Mem: 200.0},
			{Name: "c", Cpu: 30.0, Mem: 300.0},
			{Name: "d", Cpu: 40.0, Mem: 400.0},
		},
		{
			{Name: "a", Cpu: 20.0, Mem: 200.0},
			{Name: "b", Cpu: 30.0, Mem: 300.0},
			{Name: "c", Cpu: 40.0, Mem: 400.0},
			{Name: "d", Cpu: 50.0, Mem: 500.0},
		},
	}

	result := records.AverageContainerStatsSlice(input)
	sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name })

	require.Len(t, result, 4)
	assert.Equal(t, 15.0, result[0].Cpu)
	assert.Equal(t, 25.0, result[1].Cpu)
	assert.Equal(t, 35.0, result[2].Cpu)
	assert.Equal(t, 45.0, result[3].Cpu)
}
