package facts

import (
	"metis/pkg/inventory"
)

// ClassSummary captures aggregated sentinel facts per class.
type ClassSummary struct {
	Class          string                    `json:"class"`
	Nodes          []string                  `json:"nodes"`
	Kernels        map[string]int            `json:"kernels,omitempty"`
	OSImages       map[string]int            `json:"os_images,omitempty"`
	Containerd     map[string]int            `json:"containerd,omitempty"`
	K3sVersions    map[string]int            `json:"k3s_versions,omitempty"`
	PackageStats   map[string]map[string]int `json:"package_stats,omitempty"` // pkg -> version -> count
	USBMountHealth map[string]int            `json:"usb_mount_health,omitempty"`
	USBUUIDHealth  map[string]int            `json:"usb_uuid_health,omitempty"`
	USBLabelHealth map[string]int            `json:"usb_label_health,omitempty"`
	USBBindHealth  map[string]int            `json:"usb_bind_health,omitempty"`
}

// Aggregate groups snapshots by inventory class and tallies version drift.
func Aggregate(inv *inventory.Inventory, snaps []Snapshot) map[string]*ClassSummary {
	result := map[string]*ClassSummary{}
	for _, s := range snaps {
		class := "unknown"
		var scratch *inventory.USBScratchDisk
		if inv != nil {
			if node, cls, err := inv.FindNode(s.Hostname); node != nil && cls != nil && err == nil {
				class = cls.Name
				scratch = node.USBScratch
			} else if node != nil {
				scratch = node.USBScratch
			}
		}
		sum, ok := result[class]
		if !ok {
			sum = &ClassSummary{
				Class:          class,
				Kernels:        map[string]int{},
				OSImages:       map[string]int{},
				Containerd:     map[string]int{},
				K3sVersions:    map[string]int{},
				PackageStats:   map[string]map[string]int{},
				USBMountHealth: map[string]int{},
				USBUUIDHealth:  map[string]int{},
				USBLabelHealth: map[string]int{},
				USBBindHealth:  map[string]int{},
			}
			result[class] = sum
		}
		sum.Nodes = append(sum.Nodes, s.Hostname)
		if s.Kernel != "" {
			sum.Kernels[s.Kernel]++
		}
		if s.OSImage != "" {
			sum.OSImages[s.OSImage]++
		}
		if s.Containerd != "" {
			sum.Containerd[s.Containerd]++
		}
		if s.K3sVersion != "" {
			sum.K3sVersions[s.K3sVersion]++
		}
		for pkg, ver := range s.PackageSample {
			if sum.PackageStats[pkg] == nil {
				sum.PackageStats[pkg] = map[string]int{}
			}
			if ver != "" {
				sum.PackageStats[pkg][ver]++
			}
		}
		addUSBHealth(sum, scratch, s.USBScratch)
	}
	return result
}

func addUSBHealth(sum *ClassSummary, desired *inventory.USBScratchDisk, observed *USBScratch) {
	if desired == nil || sum == nil {
		return
	}
	if desired.Mountpoint != "" {
		sum.USBMountHealth[usbStatus(observed, observed != nil && observed.MountHealthy)]++
	}
	if desired.UUID != "" {
		sum.USBUUIDHealth[usbStatus(observed, observed != nil && observed.UUIDHealthy)]++
	}
	if desired.Label != "" {
		sum.USBLabelHealth[usbStatus(observed, observed != nil && observed.LabelHealthy)]++
	}
	if len(desired.BindTargets) > 0 {
		sum.USBBindHealth[usbStatus(observed, observed != nil && observed.BindHealthy)]++
	}
}

func usbStatus(observed *USBScratch, ok bool) string {
	if observed == nil {
		return "missing"
	}
	if ok {
		return "ok"
	}
	return "bad"
}
