package hpe_ilo_ahs

import (
	"bytes"
	"compress/gzip"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"git.mchus.pro/mchus/logpile/internal/models"
	"git.mchus.pro/mchus/logpile/internal/parser"
)

const (
	parserVersion = "1.0"
	ahsHeaderSize = 116
	maxGzipSize   = 50 * 1024 * 1024
)

var (
	partNumberPattern  = regexp.MustCompile(`(?i)^[a-z0-9]{1,4}\d{4,6}-[a-z0-9]{2,4}$`)
	serverSerialRE     = regexp.MustCompile(`(?i)(?:^|[_-])([a-z0-9]{10})(?:[_-]|\.)`)
	dimmSlotRE         = regexp.MustCompile(`^PROC\s+(\d+)\s+DIMM\s+(\d+)$`)
	procSlotRE         = regexp.MustCompile(`^Proc\s+(\d+)$`)
	psuSlotRE          = regexp.MustCompile(`^Power Supply\s+(\d+)$`)
	eventTimeRE        = regexp.MustCompile(`^\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}$`)
	psuXMLRE           = regexp.MustCompile(`(?s)<PowerSupplySlot id="(\d+)">(.*?)</PowerSupplySlot>`)
	firmwareLockdownRE = regexp.MustCompile(`(?s)<FirmwareLockdown>(.*?)</FirmwareLockdown>`)
	xmlFieldRE         = regexp.MustCompile(`(?s)<([A-Za-z0-9_-]+)>([^<]*)</[A-Za-z0-9_-]+>`)
	psuLogRE           = regexp.MustCompile(`Update bay (\d+) (SPN|Serial Number|Model Number|fw ver\.), value = ([A-Za-z0-9._-]+)`)
	versionFragmentRE  = regexp.MustCompile(`\d+(?:\.\d+)+`)
)

func init() {
	parser.Register(&Parser{})
}

type Parser struct{}

func (p *Parser) Name() string    { return "HPE iLO AHS Parser" }
func (p *Parser) Vendor() string  { return "hpe_ilo_ahs" }
func (p *Parser) Version() string { return parserVersion }

func (p *Parser) Detect(files []parser.ExtractedFile) int {
	if len(files) != 1 {
		return 0
	}

	file := files[0]
	if len(file.Content) < ahsHeaderSize || !bytes.HasPrefix(file.Content, []byte("ABJR")) {
		return 0
	}

	score := 55
	name := strings.ToLower(file.Path)
	if strings.HasSuffix(name, ".ahs") {
		score += 30
	}
	if bytes.Contains(file.Content, []byte("CUST_INFO.DAT")) {
		score += 10
	}
	if bytes.Contains(file.Content, []byte(".zbb")) || bytes.Contains(file.Content, []byte("ilo_boot_support.zbb")) {
		score += 10
	}
	if score > 100 {
		score = 100
	}
	return score
}

func (p *Parser) Parse(files []parser.ExtractedFile) (*models.AnalysisResult, error) {
	if len(files) == 0 {
		return emptyResult(), nil
	}

	entries, err := parseAHSContainer(files[0].Content)
	if err != nil {
		return nil, fmt.Errorf("parse ahs container: %w", err)
	}

	result := emptyResult()
	result.SourceType = models.SourceTypeArchive

	tokens := make([]string, 0, 2048)
	redfishDocs := make(map[string]map[string]any)
	rawMetadata := make([]map[string]any, 0, len(entries))

	for _, entry := range entries {
		rawMetadata = append(rawMetadata, map[string]any{
			"name":              entry.Name,
			"compressed":        entry.Compressed,
			"compressed_size":   len(entry.Payload),
			"uncompressed_size": len(entry.Content),
			"flag":              entry.Flag,
		})
		if len(entry.Content) == 0 {
			continue
		}
		tokens = append(tokens, printableTokens(entry.Content, 3)...)
		for path, doc := range extractEmbeddedRedfishDocs(entry.Content) {
			redfishDocs[path] = doc
		}
	}

	if len(rawMetadata) > 0 {
		result.RawPayloads = map[string]any{
			"hpe_ahs_entries": rawMetadata,
		}
	}

	board := parseBoardInfo(tokens, files[0].Path)
	result.Hardware.BoardInfo = board
	if board.ProductName != "" || board.SerialNumber != "" || board.PartNumber != "" {
		result.FRU = append(result.FRU, models.FRUInfo{
			Description:  "System",
			Manufacturer: board.Manufacturer,
			ProductName:  board.ProductName,
			SerialNumber: board.SerialNumber,
			PartNumber:   board.PartNumber,
			Version:      board.Version,
		})
	}

	result.Hardware.CPUs = dedupeCPUs(parseCPUs(tokens))
	result.Hardware.Memory = dedupeMemory(parseDIMMs(tokens))
	result.Hardware.PowerSupply = dedupePSUs(parsePSUs(tokens))
	result.Hardware.NetworkAdapters = dedupeNetworkAdapters(parseNetworkAdapters(tokens))
	result.Hardware.Firmware = dedupeFirmware(parseFirmware(tokens))

	psuSupplements := parsePSUSupplements(entries)
	result.Hardware.PowerSupply = dedupePSUs(mergePSUs(result.Hardware.PowerSupply, psuSupplements))

	lockdownFW, nicFirmwareByVendor := parseBCertFirmware(entries)
	result.Hardware.NetworkAdapters = dedupeNetworkAdapters(enrichNetworkAdapters(result.Hardware.NetworkAdapters, nicFirmwareByVendor))
	result.Hardware.Firmware = dedupeFirmware(append(result.Hardware.Firmware, lockdownFW...))

	storage, volumes, controllerDevices, controllerFW := parseRedfishStorage(redfishDocs)
	result.Hardware.Storage = dedupeStorage(storage)
	result.Hardware.Volumes = volumes
	result.Hardware.Firmware = dedupeFirmware(append(result.Hardware.Firmware, controllerFW...))

	result.Events = dedupeEvents(parseEvents(tokens))
	if result.CollectedAt.IsZero() {
		for _, ev := range result.Events {
			if ev.Timestamp.After(result.CollectedAt) {
				result.CollectedAt = ev.Timestamp.UTC()
			}
		}
	}

	result.Hardware.Devices = buildDevices(
		result.Hardware.BoardInfo,
		result.Hardware.CPUs,
		result.Hardware.Memory,
		result.Hardware.Storage,
		result.Hardware.NetworkAdapters,
		result.Hardware.PowerSupply,
		controllerDevices,
	)

	return result, nil
}

type ahsEntry struct {
	Name       string
	Flag       uint32
	Payload    []byte
	Content    []byte
	Compressed bool
}

func emptyResult() *models.AnalysisResult {
	return &models.AnalysisResult{
		Events:  make([]models.Event, 0),
		FRU:     make([]models.FRUInfo, 0),
		Sensors: make([]models.SensorReading, 0),
		Hardware: &models.HardwareConfig{
			Firmware:        make([]models.FirmwareInfo, 0),
			Devices:         make([]models.HardwareDevice, 0),
			CPUs:            make([]models.CPU, 0),
			Memory:          make([]models.MemoryDIMM, 0),
			Storage:         make([]models.Storage, 0),
			Volumes:         make([]models.StorageVolume, 0),
			PCIeDevices:     make([]models.PCIeDevice, 0),
			GPUs:            make([]models.GPU, 0),
			NetworkCards:    make([]models.NIC, 0),
			NetworkAdapters: make([]models.NetworkAdapter, 0),
			PowerSupply:     make([]models.PSU, 0),
		},
	}
}

func parseAHSContainer(data []byte) ([]ahsEntry, error) {
	entries := make([]ahsEntry, 0, 8)
	offset := 0

	for offset < len(data) {
		if offset+ahsHeaderSize > len(data) {
			return nil, fmt.Errorf("truncated header at offset %d", offset)
		}
		if !bytes.Equal(data[offset:offset+4], []byte("ABJR")) {
			return nil, fmt.Errorf("invalid magic at offset %d", offset)
		}

		size := int(binary.LittleEndian.Uint32(data[offset+8 : offset+12]))
		flag := binary.LittleEndian.Uint32(data[offset+16 : offset+20])
		name := strings.TrimRight(string(data[offset+20:offset+52]), "\x00")
		start := offset + ahsHeaderSize
		end := start + size
		if size < 0 || end > len(data) {
			return nil, fmt.Errorf("invalid payload size for %q", name)
		}

		payload := append([]byte(nil), data[start:end]...)
		content := payload
		compressed := len(payload) >= 2 && payload[0] == 0x1f && payload[1] == 0x8b
		if compressed {
			decoded, err := gunzipLimited(payload)
			if err == nil {
				content = decoded
			}
		}

		entries = append(entries, ahsEntry{
			Name:       name,
			Flag:       flag,
			Payload:    payload,
			Content:    content,
			Compressed: compressed,
		})
		offset = end
	}

	return entries, nil
}

func gunzipLimited(payload []byte) ([]byte, error) {
	gr, err := gzip.NewReader(bytes.NewReader(payload))
	if err != nil {
		return nil, err
	}
	defer gr.Close()

	buf, err := io.ReadAll(io.LimitReader(gr, maxGzipSize+1))
	if err != nil {
		return nil, err
	}
	if len(buf) > maxGzipSize {
		return nil, fmt.Errorf("gzip payload exceeded %d bytes", maxGzipSize)
	}
	return buf, nil
}

func printableTokens(data []byte, minLen int) []string {
	out := make([]string, 0, 256)
	start := -1
	for i, b := range data {
		if b >= 32 && b <= 126 {
			if start == -1 {
				start = i
			}
			continue
		}
		if start != -1 && i-start >= minLen {
			token := strings.TrimSpace(string(data[start:i]))
			if token != "" {
				out = append(out, token)
			}
		}
		start = -1
	}
	if start != -1 && len(data)-start >= minLen {
		token := strings.TrimSpace(string(data[start:]))
		if token != "" {
			out = append(out, token)
		}
	}
	return out
}

func extractEmbeddedRedfishDocs(data []byte) map[string]map[string]any {
	out := make(map[string]map[string]any)
	marker := []byte(`{"@odata`)
	for offset := 0; offset < len(data); {
		idx := bytes.Index(data[offset:], marker)
		if idx < 0 {
			break
		}
		start := offset + idx
		end, ok := findBalancedJSONObject(data, start)
		if !ok {
			offset = start + 1
			continue
		}

		var doc map[string]any
		if err := json.Unmarshal(data[start:end], &doc); err == nil {
			path := strings.TrimSpace(asString(doc["@odata.id"]))
			if strings.HasPrefix(path, "/redfish/") {
				out[path] = doc
			}
		}
		offset = end
	}
	return out
}

func findBalancedJSONObject(data []byte, start int) (int, bool) {
	if start >= len(data) || data[start] != '{' {
		return 0, false
	}
	depth := 0
	inString := false
	escaped := false

	for i := start; i < len(data); i++ {
		c := data[i]
		if inString {
			switch {
			case escaped:
				escaped = false
			case c == '\\':
				escaped = true
			case c == '"':
				inString = false
			}
			continue
		}

		switch c {
		case '"':
			inString = true
		case '{':
			depth++
		case '}':
			depth--
			if depth == 0 {
				return i + 1, true
			}
		}
	}

	return 0, false
}

func parseBoardInfo(tokens []string, path string) models.BoardInfo {
	var board models.BoardInfo

	for i := 0; i+3 < len(tokens); i++ {
		manufacturer := strings.TrimSpace(tokens[i])
		model := sanitizeModel(tokens[i+1])
		if !isHPEManufacturer(manufacturer) || !looksLikeServerModel(model) {
			continue
		}
		board.Manufacturer = "HPE"
		board.ProductName = model
		if isLikelySerial(tokens[i+2]) {
			board.SerialNumber = tokens[i+2]
		}
		if looksLikePartNumber(tokens[i+3]) {
			board.PartNumber = tokens[i+3]
		}
		break
	}

	if board.Manufacturer == "" && strings.Contains(strings.ToUpper(filepath.Base(path)), "HPE") {
		board.Manufacturer = "HPE"
	}
	if board.SerialNumber == "" {
		if match := serverSerialRE.FindStringSubmatch(strings.ToUpper(filepath.Base(path))); len(match) == 2 {
			board.SerialNumber = match[1]
		}
	}
	if board.ProductName == "" {
		for _, token := range tokens {
			if looksLikeServerModel(token) {
				board.ProductName = sanitizeModel(token)
				break
			}
		}
	}
	return board
}

func parseCPUs(tokens []string) []models.CPU {
	out := make([]models.CPU, 0, 2)
	for i := 0; i+2 < len(tokens); i++ {
		match := procSlotRE.FindStringSubmatch(tokens[i])
		if len(match) != 2 {
			continue
		}
		socket, _ := strconv.Atoi(match[1])
		model := ""
		manufacturer := ""
		for j := i + 1; j < len(tokens) && j <= i+5; j++ {
			if strings.HasPrefix(tokens[j], "PROC ") || procSlotRE.MatchString(tokens[j]) {
				break
			}
			if manufacturer == "" && looksLikeCPUVendor(tokens[j]) {
				manufacturer = tokens[j]
				continue
			}
			if looksLikeCPUModel(tokens[j]) {
				model = tokens[j]
				break
			}
		}
		if model == "" {
			continue
		}
		cpu := models.CPU{
			Socket:      socket,
			Model:       model,
			Description: manufacturer,
			Status:      "ok",
		}
		out = append(out, cpu)
	}
	return out
}

func parseDIMMs(tokens []string) []models.MemoryDIMM {
	out := make([]models.MemoryDIMM, 0, 16)
	for i := 0; i+3 < len(tokens); i++ {
		match := dimmSlotRE.FindStringSubmatch(tokens[i])
		if len(match) != 3 {
			continue
		}
		slot := tokens[i]
		manufacturer := tokens[i+1]
		partNumber := tokens[i+2]
		serial := tokens[i+3]
		if isUnavailable(partNumber) || isUnavailable(serial) {
			continue
		}
		if isUnavailable(manufacturer) || strings.EqualFold(manufacturer, "unknown") {
			manufacturer = ""
		}
		out = append(out, models.MemoryDIMM{
			Slot:         slot,
			Location:     slot,
			Present:      true,
			Manufacturer: manufacturer,
			PartNumber:   partNumber,
			SerialNumber: serial,
			Status:       "ok",
		})
	}
	return out
}

func parsePSUs(tokens []string) []models.PSU {
	out := make([]models.PSU, 0, 4)
	for i := 0; i < len(tokens); i++ {
		match := psuSlotRE.FindStringSubmatch(tokens[i])
		if len(match) != 2 {
			continue
		}
		slot := "PSU " + match[1]
		vendor := ""
		serial := ""
		partNumber := ""
		for j := i + 1; j < len(tokens) && j <= i+5; j++ {
			field := strings.TrimSpace(tokens[j])
			if strings.HasPrefix(field, "PciRoot(") || psuSlotRE.MatchString(field) || dimmSlotRE.MatchString(field) || procSlotRE.MatchString(field) || eventTimeRE.MatchString(field) {
				break
			}
			switch {
			case vendor == "" && looksLikePSUVendor(field):
				vendor = field
			case partNumber == "" && looksLikePartNumber(field):
				partNumber = field
			case serial == "" && isLikelySerial(field):
				serial = field
			}
		}
		if serial == "" && partNumber == "" {
			continue
		}
		psu := models.PSU{
			Slot:         slot,
			Present:      true,
			Model:        valueOr(partNumber, "Power Supply"),
			Vendor:       valueOr(cleanUnavailable(vendor), "HPE"),
			SerialNumber: cleanUnavailable(serial),
			PartNumber:   cleanUnavailable(partNumber),
			Status:       "ok",
		}
		out = append(out, psu)
	}
	return out
}

func parsePSUSupplements(entries []ahsEntry) []models.PSU {
	bySlot := make(map[string]models.PSU)

	for _, entry := range entries {
		text := string(entry.Content)
		if text == "" {
			continue
		}

		if strings.EqualFold(entry.Name, "bcert.pkg") {
			for _, match := range psuXMLRE.FindAllStringSubmatch(text, -1) {
				slotNum, _ := strconv.Atoi(match[1])
				slot := fmt.Sprintf("PSU %d", slotNum+1)
				fields := parseXMLFields(match[2])
				item := bySlot[slot]
				item.Slot = slot
				item.Present = strings.EqualFold(fields["Present"], "Yes") || item.Present
				if serial := strings.TrimSpace(fields["SerialNumber"]); serial != "" {
					item.SerialNumber = serial
				}
				if fw := strings.TrimSpace(fields["FirmwareVersion"]); fw != "" {
					item.Firmware = fw
				}
				if spare := strings.TrimSpace(fields["SparePartNumber"]); spare != "" {
					if item.Details == nil {
						item.Details = make(map[string]any)
					}
					item.Details["spare_part_number"] = spare
				}
				bySlot[slot] = item
			}
		}

		for _, match := range psuLogRE.FindAllStringSubmatch(text, -1) {
			slotNum, _ := strconv.Atoi(match[1])
			slot := fmt.Sprintf("PSU %d", slotNum+1)
			item := bySlot[slot]
			item.Slot = slot
			item.Present = true
			value := strings.TrimSpace(match[3])
			switch match[2] {
			case "SPN":
				if item.Details == nil {
					item.Details = make(map[string]any)
				}
				item.Details["spare_part_number"] = value
			case "Serial Number":
				item.SerialNumber = value
			case "Model Number":
				item.Model = value
				item.PartNumber = value
			case "fw ver.":
				item.Firmware = normalizeLooseVersion(value)
			}
			bySlot[slot] = item
		}
	}

	out := make([]models.PSU, 0, len(bySlot))
	for _, item := range bySlot {
		if item.Slot == "" {
			continue
		}
		item.Vendor = valueOr(item.Vendor, "HPE")
		item.Status = valueOr(item.Status, "ok")
		if item.Model == "" {
			item.Model = valueOr(item.PartNumber, "Power Supply")
		}
		out = append(out, item)
	}
	sort.Slice(out, func(i, j int) bool { return out[i].Slot < out[j].Slot })
	return out
}

type pcieSequence struct {
	UEFIPath string
	Code     string
	Fields   []string
}

func parseNetworkAdapters(tokens []string) []models.NetworkAdapter {
	sequences := collectPCIeSequences(tokens)
	out := make([]models.NetworkAdapter, 0, 4)

	for _, seq := range sequences {
		if strings.Contains(seq.Code, "DriveBay") {
			continue
		}
		if len(seq.Fields) == 0 {
			continue
		}

		title := seq.Fields[0]
		if strings.Contains(strings.ToLower(title), "empty") {
			continue
		}

		if !looksLikeNetworkTitle(seq.Code, title, seq.Fields) {
			continue
		}

		location := ""
		model := title
		description := ""
		partNumber := ""
		serial := ""
		firmware := ""

		for _, field := range seq.Fields[1:] {
			switch {
			case location == "" && looksLikeLocation(field):
				location = field
			case partNumber == "" && looksLikePartNumber(field):
				partNumber = field
			case serial == "" && isLikelySerial(field):
				serial = field
			case firmware == "" && looksLikeVersion(field):
				firmware = field
			case model == title && looksLikeConcreteModel(field):
				model = field
			case description == "" && field != model:
				description = field
			}
		}

		if model == "Network Controller" && description != "" {
			model, description = description, title
		}

		out = append(out, models.NetworkAdapter{
			Slot:         slotLabelFromCode(seq.Code),
			Location:     valueOr(location, slotLabelFromCode(seq.Code)),
			Present:      true,
			Model:        model,
			Description:  description,
			Vendor:       inferVendor(model),
			SerialNumber: serial,
			PartNumber:   partNumber,
			Firmware:     firmware,
			Status:       "ok",
			Details: map[string]any{
				"uefi_path": seq.UEFIPath,
				"source":    "smbios_slot_inventory",
			},
		})
	}

	return out
}

func collectPCIeSequences(tokens []string) []pcieSequence {
	out := make([]pcieSequence, 0, 16)
	for i := 0; i < len(tokens); i++ {
		if !strings.HasPrefix(tokens[i], "PciRoot(") {
			continue
		}
		if i+1 >= len(tokens) {
			continue
		}
		seq := pcieSequence{
			UEFIPath: tokens[i],
			Code:     tokens[i+1],
			Fields:   make([]string, 0, 6),
		}
		for j := i + 2; j < len(tokens) && len(seq.Fields) < 6; j++ {
			if strings.HasPrefix(tokens[j], "PciRoot(") || dimmSlotRE.MatchString(tokens[j]) || procSlotRE.MatchString(tokens[j]) || psuSlotRE.MatchString(tokens[j]) {
				break
			}
			seq.Fields = append(seq.Fields, tokens[j])
		}
		out = append(out, seq)
	}
	return out
}

func parseFirmware(tokens []string) []models.FirmwareInfo {
	out := make([]models.FirmwareInfo, 0, 8)
	seen := make(map[string]bool)

	for _, token := range tokens {
		if strings.HasPrefix(token, "iLO ") && strings.Contains(token, " built on ") {
			version := token
			build := ""
			if idx := strings.Index(token, " built on "); idx > 0 {
				version = strings.TrimSpace(token[:idx])
				build = strings.TrimSpace(token[idx+10:])
			}
			name := version
			if fields := strings.Fields(version); len(fields) >= 2 {
				name = strings.Join(fields[:2], " ")
				version = strings.TrimSpace(strings.TrimPrefix(version, name))
			}
			appendFirmware(&out, seen, models.FirmwareInfo{
				DeviceName: name,
				Version:    strings.TrimSpace(version),
				BuildTime:  build,
			})
		}
	}

	for i := 0; i+1 < len(tokens); i++ {
		name := tokens[i]
		version := tokens[i+1]
		if !isTopLevelFirmwareLabel(name) || !looksLikeVersion(version) {
			continue
		}
		appendFirmware(&out, seen, models.FirmwareInfo{
			DeviceName: name,
			Version:    version,
		})
	}

	return out
}

func parseRedfishStorage(docs map[string]map[string]any) ([]models.Storage, []models.StorageVolume, []models.HardwareDevice, []models.FirmwareInfo) {
	paths := make([]string, 0, len(docs))
	for path := range docs {
		paths = append(paths, path)
	}
	sort.Strings(paths)

	storage := make([]models.Storage, 0, 8)
	volumes := make([]models.StorageVolume, 0, 4)
	devices := make([]models.HardwareDevice, 0, 6)
	firmware := make([]models.FirmwareInfo, 0, 8)
	fabricNames := make(map[string]string)
	fabricTypes := make(map[string]string)

	for _, path := range paths {
		doc := docs[path]
		docType := asString(doc["@odata.type"])
		switch {
		case strings.Contains(docType, "#Fabric."):
			fabricID := redfishID(path)
			fabricNames[fabricID] = strings.TrimSpace(asString(doc["Name"]))
			fabricTypes[fabricID] = strings.TrimSpace(asString(doc["FabricType"]))

		case strings.Contains(docType, "#Switch."):
			fabricID := fabricIDFromPath(path)
			name := valueOr(fabricNames[fabricID], strings.TrimSpace(asString(doc["Name"])))
			model := strings.TrimSpace(asString(doc["Model"]))
			fw := strings.TrimSpace(asString(doc["FirmwareVersion"]))
			device := models.HardwareDevice{
				ID:          "hpe-fabric-" + redfishID(path),
				Kind:        models.DeviceKindStorage,
				Source:      "redfish",
				Slot:        valueOr(fabricID, redfishID(path)),
				DeviceClass: "storage_backplane",
				Model:       valueOr(name, model),
				PartNumber:  model,
				Firmware:    fw,
				Status:      redfishStatus(doc["Status"]),
				Details: map[string]any{
					"odata_id":             path,
					"fabric_type":          valueOr(fabricTypes[fabricID], strings.TrimSpace(asString(doc["FabricType"]))),
					"switch_type":          strings.TrimSpace(asString(doc["SwitchType"])),
					"supported_protocols":  stringSlice(doc["SupportedProtocols"]),
					"domain_id":            asInt64(doc["DomainID"]),
					"fabric_name":          fabricNames[fabricID],
					"connected_chassis_id": asString(nested(doc, "Links", "Chassis", "@odata.id")),
				},
			}
			devices = append(devices, device)
			if fw != "" {
				firmware = append(firmware, models.FirmwareInfo{
					DeviceName: valueOr(name, model),
					Version:    fw,
				})
			}

		case strings.Contains(docType, "#StorageController."):
			slot := redfishServiceLabel(doc, "Location", "PartLocation", "ServiceLabel")
			model := valueOr(asString(doc["Model"]), asString(doc["Name"]))
			partNumber := strings.TrimSpace(asString(doc["PartNumber"]))
			sku := strings.TrimSpace(asString(doc["SKU"]))
			serial := strings.TrimSpace(asString(doc["SerialNumber"]))
			fw := strings.TrimSpace(asString(doc["FirmwareVersion"]))
			device := models.HardwareDevice{
				ID:           "hpe-ctrl-" + redfishID(path),
				Kind:         models.DeviceKindStorage,
				Source:       "redfish",
				Slot:         slot,
				Location:     slot,
				DeviceClass:  "storage_controller",
				Model:        model,
				PartNumber:   valueOr(partNumber, sku),
				Manufacturer: strings.TrimSpace(asString(doc["Manufacturer"])),
				SerialNumber: serial,
				Firmware:     fw,
				Status:       redfishStatus(doc["Status"]),
				Details: map[string]any{
					"odata_id":                       path,
					"part_number":                    partNumber,
					"sku":                            sku,
					"speed_gbps":                     asFloat64(doc["SpeedGbps"]),
					"supported_controller_protocols": stringSlice(doc["SupportedControllerProtocols"]),
					"supported_device_protocols":     stringSlice(doc["SupportedDeviceProtocols"]),
					"supported_raid_types":           stringSlice(doc["SupportedRAIDTypes"]),
					"cache_total_mib":                asInt64(nested(doc, "CacheSummary", "TotalCacheSizeMiB")),
					"persistent_cache_mib":           asInt64(nested(doc, "CacheSummary", "PersistentCacheSizeMiB")),
					"durable_name":                   firstDurableName(doc),
				},
			}
			if width := asInt(doc, "PCIeInterface", "LanesInUse"); width > 0 {
				device.LinkWidth = width
			}
			if speed := strings.TrimSpace(asString(nested(doc, "PCIeInterface", "PCIeType"))); speed != "" {
				device.LinkSpeed = speed
			}
			devices = append(devices, device)
			if fw != "" {
				firmware = append(firmware, models.FirmwareInfo{
					DeviceName:  model,
					Description: slot,
					Version:     fw,
				})
			}

		case strings.Contains(docType, "#Drive."):
			if strings.EqualFold(redfishStatus(doc["Status"]), "absent") {
				continue
			}
			capacity := asInt64(doc["CapacityBytes"])
			slot := redfishServiceLabel(doc, "PhysicalLocation", "PartLocation", "ServiceLabel")
			if slot == "" {
				slot = redfishServiceLabel(doc, "Location", "PartLocation", "ServiceLabel")
			}
			endurance := asOptionalInt(doc["PredictedMediaLifeLeftPercent"])
			entry := models.Storage{
				Slot:                  slot,
				Type:                  valueOr(asString(doc["MediaType"]), "Drive"),
				Model:                 valueOr(asString(doc["Model"]), asString(doc["Name"])),
				Description:           strings.TrimSpace(asString(doc["Name"])),
				SizeGB:                bytesToDecimalGB(capacity),
				SerialNumber:          strings.TrimSpace(asString(doc["SerialNumber"])),
				Firmware:              strings.TrimSpace(asString(doc["Revision"])),
				Interface:             valueOr(asString(doc["Protocol"]), asString(doc["MediaType"])),
				Present:               true,
				RemainingEndurancePct: endurance,
				Status:                redfishStatus(doc["Status"]),
				Details: map[string]any{
					"odata_id":                  path,
					"capacity_bytes":            capacity,
					"failure_predicted":         asBool(doc["FailurePredicted"]),
					"negotiated_speed_gbps":     asFloat64(doc["NegotiatedSpeedGbs"]),
					"capable_speed_gbps":        asFloat64(doc["CapableSpeedGbs"]),
					"location_indicator_active": asBool(doc["LocationIndicatorActive"]),
				},
			}
			storage = append(storage, entry)

		case strings.Contains(docType, "#Volume.") && !strings.HasSuffix(path, "/Capabilities"):
			volumes = append(volumes, models.StorageVolume{
				ID:            strings.TrimSpace(asString(doc["Id"])),
				Name:          strings.TrimSpace(asString(doc["Name"])),
				RAIDLevel:     strings.TrimSpace(asString(doc["RAIDType"])),
				CapacityBytes: asInt64(doc["CapacityBytes"]),
				SizeGB:        bytesToDecimalGB(asInt64(doc["CapacityBytes"])),
				Status:        redfishStatus(doc["Status"]),
			})
		}
	}

	return storage, dedupeVolumes(volumes), dedupeDevices(devices), dedupeFirmware(firmware)
}

func buildDevices(board models.BoardInfo, cpus []models.CPU, memory []models.MemoryDIMM, storage []models.Storage, adapters []models.NetworkAdapter, psus []models.PSU, extras []models.HardwareDevice) []models.HardwareDevice {
	devices := make([]models.HardwareDevice, 0, 1+len(cpus)+len(memory)+len(storage)+len(adapters)+len(psus)+len(extras))

	if board.ProductName != "" || board.SerialNumber != "" {
		devices = append(devices, models.HardwareDevice{
			ID:           "hpe-board",
			Kind:         models.DeviceKindBoard,
			Source:       "smbios",
			Model:        board.ProductName,
			Manufacturer: board.Manufacturer,
			SerialNumber: board.SerialNumber,
			PartNumber:   board.PartNumber,
			Status:       "ok",
		})
	}

	for _, cpu := range cpus {
		devices = append(devices, models.HardwareDevice{
			ID:           fmt.Sprintf("hpe-cpu-%d", cpu.Socket),
			Kind:         models.DeviceKindCPU,
			Source:       "smbios",
			Slot:         fmt.Sprintf("CPU %d", cpu.Socket),
			Model:        cpu.Model,
			Manufacturer: strings.TrimSpace(cpu.Description),
			Cores:        cpu.Cores,
			Threads:      cpu.Threads,
			FrequencyMHz: cpu.FrequencyMHz,
			MaxFreqMHz:   cpu.MaxFreqMHz,
			Status:       cpu.Status,
		})
	}

	for _, dimm := range memory {
		devices = append(devices, models.HardwareDevice{
			ID:           "hpe-mem-" + sanitizeID(dimm.Slot),
			Kind:         models.DeviceKindMemory,
			Source:       "smbios",
			Slot:         dimm.Slot,
			Location:     dimm.Location,
			Model:        dimm.PartNumber,
			Manufacturer: dimm.Manufacturer,
			SerialNumber: dimm.SerialNumber,
			PartNumber:   dimm.PartNumber,
			Present:      boolPtr(dimm.Present),
			Status:       dimm.Status,
		})
	}

	for _, disk := range storage {
		devices = append(devices, models.HardwareDevice{
			ID:                    "hpe-disk-" + sanitizeID(valueOr(disk.SerialNumber, disk.Slot)),
			Kind:                  models.DeviceKindStorage,
			Source:                "redfish",
			Slot:                  disk.Slot,
			Location:              disk.Location,
			Model:                 disk.Model,
			Manufacturer:          disk.Manufacturer,
			SerialNumber:          disk.SerialNumber,
			Firmware:              disk.Firmware,
			Type:                  disk.Type,
			Interface:             disk.Interface,
			Present:               boolPtr(disk.Present),
			SizeGB:                disk.SizeGB,
			Status:                disk.Status,
			RemainingEndurancePct: disk.RemainingEndurancePct,
		})
	}

	for _, nic := range adapters {
		devices = append(devices, models.HardwareDevice{
			ID:           "hpe-net-" + sanitizeID(valueOr(nic.SerialNumber, nic.Slot+"-"+nic.Model)),
			Kind:         models.DeviceKindNetwork,
			Source:       "smbios",
			Slot:         nic.Slot,
			Location:     nic.Location,
			Model:        nic.Model,
			Manufacturer: nic.Vendor,
			SerialNumber: nic.SerialNumber,
			PartNumber:   nic.PartNumber,
			Firmware:     nic.Firmware,
			PortCount:    nic.PortCount,
			PortType:     nic.PortType,
			MACAddresses: append([]string(nil), nic.MACAddresses...),
			Present:      boolPtr(nic.Present),
			Status:       nic.Status,
		})
	}

	for _, psu := range psus {
		devices = append(devices, models.HardwareDevice{
			ID:           "hpe-psu-" + sanitizeID(valueOr(psu.SerialNumber, psu.Slot)),
			Kind:         models.DeviceKindPSU,
			Source:       "smbios",
			Slot:         psu.Slot,
			Model:        psu.Model,
			Manufacturer: psu.Vendor,
			SerialNumber: psu.SerialNumber,
			PartNumber:   psu.PartNumber,
			Firmware:     psu.Firmware,
			WattageW:     psu.WattageW,
			InputType:    psu.InputType,
			Present:      boolPtr(psu.Present),
			Status:       psu.Status,
		})
	}

	devices = append(devices, extras...)
	return dedupeDevices(devices)
}

func parseEvents(tokens []string) []models.Event {
	out := make([]models.Event, 0, 16)
	for i := 0; i+1 < len(tokens); i++ {
		if !eventTimeRE.MatchString(tokens[i]) {
			continue
		}
		ts, err := time.ParseInLocation("01/02/2006 15:04:05", tokens[i], time.UTC)
		if err != nil {
			continue
		}

		message := ""
		for j := i + 1; j < len(tokens) && j <= i+4; j++ {
			if eventTimeRE.MatchString(tokens[j]) {
				break
			}
			if looksLikeEventMessage(tokens[j]) {
				message = tokens[j]
				break
			}
		}
		if message == "" {
			continue
		}

		out = append(out, models.Event{
			Timestamp:   ts.UTC(),
			Source:      "HPE iLO",
			EventType:   inferEventType(message),
			Severity:    inferSeverity(message),
			Description: message,
			RawData:     message,
		})
	}
	return out
}

func appendFirmware(dst *[]models.FirmwareInfo, seen map[string]bool, item models.FirmwareInfo) {
	item.DeviceName = strings.TrimSpace(item.DeviceName)
	item.Version = strings.TrimSpace(item.Version)
	if item.DeviceName == "" || item.Version == "" {
		return
	}
	key := item.DeviceName + "|" + item.Version + "|" + item.Description
	if seen[key] {
		return
	}
	seen[key] = true
	*dst = append(*dst, item)
}

func dedupeCPUs(items []models.CPU) []models.CPU {
	seen := make(map[string]bool)
	out := make([]models.CPU, 0, len(items))
	for _, item := range items {
		key := fmt.Sprintf("%d|%s", item.Socket, item.Model)
		if seen[key] {
			continue
		}
		seen[key] = true
		out = append(out, item)
	}
	return out
}

func dedupeMemory(items []models.MemoryDIMM) []models.MemoryDIMM {
	seen := make(map[string]bool)
	out := make([]models.MemoryDIMM, 0, len(items))
	for _, item := range items {
		key := valueOr(item.SerialNumber, item.Slot+"|"+item.PartNumber)
		if seen[key] {
			continue
		}
		seen[key] = true
		out = append(out, item)
	}
	return out
}

func dedupePSUs(items []models.PSU) []models.PSU {
	seen := make(map[string]bool)
	out := make([]models.PSU, 0, len(items))
	for _, item := range items {
		key := valueOr(item.SerialNumber, item.Slot+"|"+item.PartNumber)
		if seen[key] {
			continue
		}
		seen[key] = true
		out = append(out, item)
	}
	return out
}

func dedupeNetworkAdapters(items []models.NetworkAdapter) []models.NetworkAdapter {
	seen := make(map[string]bool)
	out := make([]models.NetworkAdapter, 0, len(items))
	for _, item := range items {
		key := valueOr(item.SerialNumber, item.Slot+"|"+item.Model)
		if seen[key] {
			continue
		}
		seen[key] = true
		out = append(out, item)
	}
	return out
}

func dedupeStorage(items []models.Storage) []models.Storage {
	seen := make(map[string]bool)
	out := make([]models.Storage, 0, len(items))
	for _, item := range items {
		key := valueOr(item.SerialNumber, item.Slot+"|"+item.Model)
		if seen[key] {
			continue
		}
		seen[key] = true
		out = append(out, item)
	}
	return out
}

func dedupeFirmware(items []models.FirmwareInfo) []models.FirmwareInfo {
	seen := make(map[string]bool)
	out := make([]models.FirmwareInfo, 0, len(items))
	for _, item := range items {
		key := item.DeviceName + "|" + item.Version + "|" + item.Description
		if seen[key] {
			continue
		}
		seen[key] = true
		out = append(out, item)
	}
	return out
}

func dedupeVolumes(items []models.StorageVolume) []models.StorageVolume {
	seen := make(map[string]bool)
	out := make([]models.StorageVolume, 0, len(items))
	for _, item := range items {
		key := valueOr(item.ID, item.Name+"|"+item.Controller)
		if seen[key] {
			continue
		}
		seen[key] = true
		out = append(out, item)
	}
	return out
}

func dedupeDevices(items []models.HardwareDevice) []models.HardwareDevice {
	seen := make(map[string]bool)
	out := make([]models.HardwareDevice, 0, len(items))
	for _, item := range items {
		key := valueOr(item.SerialNumber, item.Kind+"|"+item.Slot+"|"+item.Model)
		if seen[key] {
			continue
		}
		seen[key] = true
		out = append(out, item)
	}
	return out
}

func dedupeEvents(items []models.Event) []models.Event {
	seen := make(map[string]bool)
	out := make([]models.Event, 0, len(items))
	for _, item := range items {
		key := item.Timestamp.Format(time.RFC3339) + "|" + item.Description
		if seen[key] {
			continue
		}
		seen[key] = true
		out = append(out, item)
	}
	return out
}

func isHPEManufacturer(v string) bool {
	v = strings.TrimSpace(strings.ToUpper(v))
	return v == "HPE" || v == "HP"
}

func looksLikePSUVendor(v string) bool {
	v = strings.TrimSpace(strings.ToUpper(v))
	switch v {
	case "HPE", "HP", "DELTA", "LITEON", "LTEON":
		return true
	default:
		return false
	}
}

func looksLikeServerModel(v string) bool {
	v = sanitizeModel(v)
	if v == "" {
		return false
	}
	lower := strings.ToLower(v)
	return strings.Contains(lower, "proliant") || strings.Contains(lower, "apollo") || strings.Contains(lower, "synergy") || strings.Contains(lower, "edgeline")
}

func looksLikeCPUVendor(v string) bool {
	return strings.Contains(v, "Intel") || strings.Contains(v, "AMD")
}

func looksLikeCPUModel(v string) bool {
	return strings.Contains(v, "Xeon") || strings.Contains(v, "EPYC") || strings.Contains(v, "Opteron")
}

func isUnavailable(v string) bool {
	v = strings.TrimSpace(strings.ToUpper(v))
	return v == "" || v == "NOT AVAILABLE" || v == "UNKNOWN" || v == "N/A"
}

func cleanUnavailable(v string) string {
	if isUnavailable(v) {
		return ""
	}
	return strings.TrimSpace(v)
}

func looksLikePartNumber(v string) bool {
	return partNumberPattern.MatchString(strings.TrimSpace(v))
}

func isLikelySerial(v string) bool {
	v = strings.TrimSpace(v)
	if len(v) < 6 || len(v) > 24 || strings.Contains(v, "-") || isUnavailable(v) {
		return false
	}
	for _, r := range v {
		if (r < '0' || r > '9') && (r < 'A' || r > 'Z') && (r < 'a' || r > 'z') {
			return false
		}
	}
	return true
}

func looksLikeLocation(v string) bool {
	lower := strings.ToLower(strings.TrimSpace(v))
	return strings.HasPrefix(lower, "slot ") || strings.HasPrefix(lower, "ocp slot") || strings.HasPrefix(lower, "pci-e slot") || strings.HasPrefix(lower, "pci-e") || strings.HasPrefix(lower, "nvme drive")
}

func looksLikeVersion(v string) bool {
	v = strings.TrimSpace(v)
	if len(v) < 3 || len(v) > 48 || isUnavailable(v) {
		return false
	}
	if strings.HasPrefix(v, "v") && len(v) > 1 && v[1] >= '0' && v[1] <= '9' {
		return true
	}
	digit := false
	for _, r := range v {
		if r >= '0' && r <= '9' {
			digit = true
			break
		}
	}
	if !digit {
		return false
	}
	return strings.Contains(v, ".") || strings.Contains(strings.ToLower(v), "build")
}

func looksLikeConcreteModel(v string) bool {
	if isUnavailable(v) || looksLikeVersion(v) || looksLikePartNumber(v) || isLikelySerial(v) {
		return false
	}
	if looksLikeLocation(v) {
		return false
	}
	return true
}

func looksLikeNetworkTitle(code, title string, fields []string) bool {
	lower := strings.ToLower(code + " " + title + " " + strings.Join(fields, " "))
	return strings.Contains(lower, "nic.") || strings.Contains(lower, "network controller") || strings.Contains(lower, "ethernet") || strings.Contains(lower, "broadcom") || strings.Contains(lower, "connectx") || strings.Contains(lower, "mellanox") || strings.Contains(lower, "ocp.slot.15")
}

func isTopLevelFirmwareLabel(v string) bool {
	switch strings.TrimSpace(v) {
	case "System ROM", "Redundant System ROM", "Server Platform Services (SPS) Firmware", "Intelligent Platform Abstraction Data":
		return true
	default:
		return false
	}
}

func inferVendor(model string) string {
	lower := strings.ToLower(model)
	switch {
	case strings.Contains(lower, "broadcom"):
		return "Broadcom"
	case strings.Contains(lower, "mellanox"), strings.Contains(lower, "connectx"), strings.Contains(lower, "mcx"):
		return "NVIDIA"
	case strings.Contains(lower, "hpe"):
		return "HPE"
	default:
		return ""
	}
}

func mergePSUs(base, extra []models.PSU) []models.PSU {
	merged := make(map[string]models.PSU)
	order := make([]string, 0, len(base)+len(extra))
	mergeOne := func(item models.PSU) {
		key := strings.ToLower(strings.TrimSpace(item.Slot))
		if key == "" {
			key = strings.ToLower(strings.TrimSpace(valueOr(item.SerialNumber, item.Model+"|"+item.PartNumber)))
		}
		if key == "" {
			return
		}
		current, exists := merged[key]
		if !exists {
			merged[key] = item
			order = append(order, key)
			return
		}
		if current.Slot == "" {
			current.Slot = item.Slot
		}
		current.Present = current.Present || item.Present
		current.Model = valueOr(current.Model, item.Model)
		current.Description = valueOr(current.Description, item.Description)
		current.Vendor = valueOr(current.Vendor, item.Vendor)
		if current.WattageW == 0 {
			current.WattageW = item.WattageW
		}
		current.SerialNumber = valueOr(current.SerialNumber, item.SerialNumber)
		current.PartNumber = valueOr(current.PartNumber, item.PartNumber)
		current.Firmware = valueOr(current.Firmware, item.Firmware)
		current.Status = valueOr(current.Status, item.Status)
		current.InputType = valueOr(current.InputType, item.InputType)
		if current.InputPowerW == 0 {
			current.InputPowerW = item.InputPowerW
		}
		if current.OutputPowerW == 0 {
			current.OutputPowerW = item.OutputPowerW
		}
		if current.InputVoltage == 0 {
			current.InputVoltage = item.InputVoltage
		}
		if current.OutputVoltage == 0 {
			current.OutputVoltage = item.OutputVoltage
		}
		if current.TemperatureC == 0 {
			current.TemperatureC = item.TemperatureC
		}
		current.Details = mergeDetailMaps(current.Details, item.Details)
		merged[key] = current
	}
	for _, item := range base {
		mergeOne(item)
	}
	for _, item := range extra {
		mergeOne(item)
	}
	out := make([]models.PSU, 0, len(order))
	for _, key := range order {
		out = append(out, merged[key])
	}
	return out
}

func enrichNetworkAdapters(items []models.NetworkAdapter, firmwareByVendor map[string]string) []models.NetworkAdapter {
	out := make([]models.NetworkAdapter, 0, len(items))
	for _, item := range items {
		if item.Firmware == "" {
			if fw := firmwareByVendor[strings.ToLower(strings.TrimSpace(item.Vendor))]; fw != "" {
				item.Firmware = fw
			}
		}
		out = append(out, item)
	}
	return out
}

func parseBCertFirmware(entries []ahsEntry) ([]models.FirmwareInfo, map[string]string) {
	out := make([]models.FirmwareInfo, 0, 8)
	nicFirmwareByVendor := make(map[string]string)
	seen := make(map[string]bool)

	tagNames := map[string]string{
		"SystemProgrammableLogicDevice":       "System Programmable Logic Device",
		"ServerPlatformServicesSPSFirmware":   "Server Platform Services (SPS) Firmware",
		"STMicroGen11TPM":                     "TPM Firmware",
		"PrimaryR012U3x16slotsriserx8-x16-x8": "PCIe Riser 1 Programmable Logic Device",
		"HPEMR408i-oGen11":                    "HPE MR408i-o Gen11",
		"UBM3":                                "8 SFF 24G x1NVMe/SAS UBM3 BC BP",
		"BCM57191Gb4pBASE-T":                  "BCM 5719 1Gb 4p BASE-T OCP Adptr",
		"BCM57191Gb4pBASE-TOCP3":              "BCM 5719 1Gb 4p BASE-T OCP Adptr",
	}

	for _, entry := range entries {
		if !strings.EqualFold(entry.Name, "bcert.pkg") {
			continue
		}
		text := string(entry.Content)
		for _, match := range firmwareLockdownRE.FindAllStringSubmatch(text, -1) {
			fields := parseXMLFields(match[1])
			for tag, value := range fields {
				name := tagNames[tag]
				if name == "" {
					continue
				}
				version := normalizeBCertVersion(tag, value)
				if version == "" {
					continue
				}
				appendFirmware(&out, seen, models.FirmwareInfo{
					DeviceName: name,
					Version:    version,
				})
				if strings.Contains(name, "BCM 5719") {
					nicFirmwareByVendor["broadcom"] = version
				}
			}
		}
	}

	return out, nicFirmwareByVendor
}

func parseXMLFields(block string) map[string]string {
	out := make(map[string]string)
	for _, match := range xmlFieldRE.FindAllStringSubmatch(block, -1) {
		out[match[1]] = strings.TrimSpace(match[2])
	}
	return out
}

func normalizeBCertVersion(tag, value string) string {
	value = strings.TrimSpace(value)
	if value == "" || strings.EqualFold(value, "NA") {
		return ""
	}
	switch tag {
	case "UBM3":
		if idx := strings.LastIndex(value, "/"); idx >= 0 && idx+1 < len(value) {
			return strings.TrimSpace(value[idx+1:])
		}
	case "IntegratedLights-OutVI":
		if idx := strings.Index(value, " - "); idx > 0 {
			return strings.TrimSpace(value[:idx])
		}
	case "U54":
		return value
	}
	return value
}

func normalizeLooseVersion(value string) string {
	if match := versionFragmentRE.FindString(strings.TrimSpace(value)); match != "" {
		return match
	}
	return strings.TrimSpace(value)
}

func slotLabelFromCode(code string) string {
	parts := strings.Split(code, ".")
	if len(parts) < 3 {
		return code
	}
	switch parts[0] {
	case "NIC":
		return "Slot " + parts[2]
	case "OCP":
		return "OCP Slot " + parts[2]
	case "PCI":
		return "PCI-E Slot " + parts[2]
	default:
		return code
	}
}

func fabricIDFromPath(path string) string {
	parts := strings.Split(strings.Trim(path, "/"), "/")
	for i := 0; i+1 < len(parts); i++ {
		if parts[i] == "Fabrics" {
			return parts[i+1]
		}
	}
	return ""
}

func inferSeverity(message string) models.Severity {
	lower := strings.ToLower(message)
	switch {
	case strings.Contains(lower, " down"), strings.Contains(lower, "warning"), strings.Contains(lower, "fail"), strings.Contains(lower, "error"):
		return models.SeverityWarning
	default:
		return models.SeverityInfo
	}
}

func inferEventType(message string) string {
	lower := strings.ToLower(message)
	switch {
	case strings.Contains(lower, "login"):
		return "Login"
	case strings.Contains(lower, "logout"):
		return "Logout"
	case strings.Contains(lower, "network"):
		return "Network"
	case strings.Contains(lower, "license"):
		return "License"
	default:
		return "Event"
	}
}

func looksLikeEventMessage(v string) bool {
	if len(v) < 8 || strings.HasPrefix(v, "src/") || strings.HasPrefix(v, "PciRoot(") {
		return false
	}
	lower := strings.ToLower(v)
	return strings.Contains(lower, "login") || strings.Contains(lower, "logout") || strings.Contains(lower, "link") || strings.Contains(lower, "license") || strings.Contains(lower, "security state")
}

func sanitizeModel(v string) string {
	return strings.TrimSuffix(strings.TrimSpace(v), ":")
}

func sanitizeID(v string) string {
	v = strings.ToLower(strings.TrimSpace(v))
	v = strings.ReplaceAll(v, " ", "-")
	v = strings.ReplaceAll(v, "/", "-")
	v = strings.ReplaceAll(v, ".", "-")
	return v
}

func bytesToDecimalGB(size int64) int {
	if size <= 0 {
		return 0
	}
	return int((size + 500_000_000) / 1_000_000_000)
}

func redfishServiceLabel(doc map[string]any, path ...string) string {
	return strings.TrimSpace(asString(nested(doc, path...)))
}

func redfishStatus(v any) string {
	status, _ := v.(map[string]any)
	state := strings.TrimSpace(asString(status["State"]))
	health := strings.TrimSpace(asString(status["Health"]))
	if strings.EqualFold(state, "Absent") {
		return "absent"
	}
	if strings.EqualFold(health, "Warning") || strings.EqualFold(health, "Critical") {
		return strings.ToLower(health)
	}
	if state != "" {
		return strings.ToLower(state)
	}
	if health != "" {
		return strings.ToLower(health)
	}
	return ""
}

func redfishID(path string) string {
	parts := strings.Split(strings.Trim(path, "/"), "/")
	if len(parts) == 0 {
		return "unknown"
	}
	return sanitizeID(parts[len(parts)-1])
}

func nested(v any, path ...string) any {
	cur := v
	for _, key := range path {
		m, ok := cur.(map[string]any)
		if !ok {
			return nil
		}
		cur = m[key]
	}
	return cur
}

func asString(v any) string {
	switch value := v.(type) {
	case string:
		return value
	case fmt.Stringer:
		return value.String()
	default:
		return ""
	}
}

func asInt(doc map[string]any, path ...string) int {
	return int(asInt64(nested(doc, path...)))
}

func asInt64(v any) int64 {
	switch value := v.(type) {
	case float64:
		return int64(value)
	case float32:
		return int64(value)
	case int:
		return int64(value)
	case int64:
		return value
	case json.Number:
		n, _ := value.Int64()
		return n
	default:
		return 0
	}
}

func asFloat64(v any) float64 {
	switch t := v.(type) {
	case float64:
		return t
	case float32:
		return float64(t)
	case int:
		return float64(t)
	case int64:
		return float64(t)
	case json.Number:
		f, _ := t.Float64()
		return f
	default:
		return 0
	}
}

func asOptionalInt(v any) *int {
	switch value := v.(type) {
	case float64:
		out := int(value)
		return &out
	case int:
		out := value
		return &out
	default:
		return nil
	}
}

func asBool(v any) bool {
	b, ok := v.(bool)
	return ok && b
}

func valueOr(v, fallback string) string {
	if strings.TrimSpace(v) != "" {
		return strings.TrimSpace(v)
	}
	return strings.TrimSpace(fallback)
}

func stringSlice(v any) []string {
	items, ok := v.([]any)
	if !ok {
		return nil
	}
	out := make([]string, 0, len(items))
	for _, item := range items {
		value := strings.TrimSpace(asString(item))
		if value == "" {
			continue
		}
		out = append(out, value)
	}
	return out
}

func firstDurableName(doc map[string]any) string {
	items, ok := doc["Identifiers"].([]any)
	if !ok {
		return ""
	}
	for _, item := range items {
		entry, ok := item.(map[string]any)
		if !ok {
			continue
		}
		if value := strings.TrimSpace(asString(entry["DurableName"])); value != "" {
			return value
		}
	}
	return ""
}

func mergeDetailMaps(base, extra map[string]any) map[string]any {
	if len(extra) == 0 {
		return base
	}
	if base == nil {
		base = make(map[string]any, len(extra))
	}
	for key, value := range extra {
		if _, exists := base[key]; !exists || isZeroValue(base[key]) {
			base[key] = value
		}
	}
	return base
}

func isZeroValue(v any) bool {
	switch t := v.(type) {
	case nil:
		return true
	case string:
		return strings.TrimSpace(t) == ""
	case int:
		return t == 0
	case int64:
		return t == 0
	case float64:
		return t == 0
	case bool:
		return !t
	default:
		return false
	}
}

func boolPtr(v bool) *bool {
	out := v
	return &out
}
