package collector

import (
	"bee/audit/internal/schema"
	"log/slog"
	"strings"
)

// collectMemory runs dmidecode -t 17 and returns all memory slots.
func collectMemory() []schema.HardwareMemory {
	out, err := runDmidecode("17")
	if err != nil {
		slog.Warn("memory: dmidecode type 17 failed", "err", err)
		return nil
	}
	dimms := parseMemory(out)
	slog.Info("memory: collected", "count", len(dimms))
	return dimms
}

func parseMemory(output string) []schema.HardwareMemory {
	sections := splitDMISections(output, "Memory Device")
	dimms := make([]schema.HardwareMemory, 0, len(sections))
	for _, fields := range sections {
		dimm := parseMemorySection(fields)
		dimms = append(dimms, dimm)
	}
	return dimms
}

func parseMemorySection(fields map[string]string) schema.HardwareMemory {
	dimm := schema.HardwareMemory{}

	if v := cleanDMIValue(fields["Locator"]); v != "" {
		dimm.Slot = &v
	}
	if v := cleanDMIValue(fields["Bank Locator"]); v != "" {
		dimm.Location = &v
	}

	// presence: "No Module Installed" or size == 0
	present := true
	rawSize := fields["Size"]
	if strings.Contains(strings.ToLower(rawSize), "no module") || rawSize == "0" || rawSize == "" {
		present = false
	}
	dimm.Present = &present

	if !present {
		status := statusEmpty
		dimm.Status = &status
		return dimm
	}

	status := statusOK
	dimm.Status = &status

	if mb := parseMemorySizeMB(rawSize); mb > 0 {
		dimm.SizeMB = &mb
	}
	if v := cleanDMIValue(fields["Type"]); v != "" && v != "Unknown" {
		dimm.Type = &v
	}
	if mhz := parseInt(strings.TrimSuffix(fields["Speed"], " MT/s")); mhz > 0 {
		dimm.MaxSpeedMHz = &mhz
	}
	if mhz := parseInt(strings.TrimSuffix(fields["Configured Memory Speed"], " MT/s")); mhz > 0 {
		dimm.CurrentSpeedMHz = &mhz
	}
	if v := cleanDMIValue(fields["Manufacturer"]); v != "" {
		dimm.Manufacturer = &v
	}
	if v := cleanDMIValue(fields["Serial Number"]); v != "" {
		dimm.SerialNumber = &v
	}
	if v := cleanDMIValue(fields["Part Number"]); v != "" {
		p := strings.TrimSpace(v)
		dimm.PartNumber = &p
	}

	return dimm
}

// parseMemorySizeMB parses DMI size strings: "8 GB", "2048 MB", "No Module Installed".
func parseMemorySizeMB(s string) int {
	s = strings.TrimSpace(s)
	if strings.Contains(strings.ToLower(s), "no module") {
		return 0
	}
	if strings.HasSuffix(s, " GB") {
		n := parseInt(strings.TrimSuffix(s, " GB"))
		return n * 1024
	}
	if strings.HasSuffix(s, " MB") {
		return parseInt(strings.TrimSuffix(s, " MB"))
	}
	return parseInt(s)
}
