package exporter

import (
	"fmt"
	"net/url"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

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

var cpuMicrocodeFirmwareRegex = regexp.MustCompile(`(?i)^cpu\d+\s+microcode$`)
var cpuMicrocodeFirmwareCaptureRegex = regexp.MustCompile(`(?i)^cpu(\d+)\s+microcode$`)

// ConvertToReanimator converts AnalysisResult to Reanimator export format
func ConvertToReanimator(result *models.AnalysisResult) (*ReanimatorExport, error) {
	if result == nil {
		return nil, fmt.Errorf("no data available for export")
	}

	if result.Hardware == nil {
		return nil, fmt.Errorf("no hardware data available for export")
	}

	if result.Hardware.BoardInfo.SerialNumber == "" {
		return nil, fmt.Errorf("board serial_number is required for Reanimator export")
	}

	// Determine target host (optional field)
	targetHost := inferTargetHost(result.TargetHost, result.Filename)

	collectedAt := formatRFC3339(reanimatorCollectedAt(result))
	devices := canonicalDevicesForExport(result.Hardware)

	export := &ReanimatorExport{
		Filename:    result.Filename,
		SourceType:  normalizeSourceType(result.SourceType),
		Protocol:    normalizeProtocol(result.Protocol),
		TargetHost:  targetHost,
		CollectedAt: collectedAt,
		Hardware: ReanimatorHardware{
			Board:         convertBoard(result.Hardware.BoardInfo),
			Firmware:      dedupeFirmware(convertFirmware(result.Hardware.Firmware)),
			CPUs:          dedupeCPUs(convertCPUsFromDevices(devices, collectedAt, result.Hardware.BoardInfo.SerialNumber, buildCPUMicrocodeBySocket(result.Hardware.Firmware))),
			Memory:        dedupeMemory(convertMemoryFromDevices(devices, collectedAt)),
			Storage:       dedupeStorage(convertStorageFromDevices(devices, collectedAt)),
			PCIeDevices:   dedupePCIe(convertPCIeFromDevices(devices, collectedAt)),
			PowerSupplies: dedupePSUs(convertPSUsFromDevices(devices, collectedAt)),
			Sensors:       convertSensors(result.Sensors),
			EventLogs:     convertEventLogs(result.Events, collectedAt),
		},
	}

	return export, nil
}

// reanimatorCollectedAt returns the best timestamp for Reanimator export collected_at.
// Prefers InventoryLastModifiedAt when it is set and no older than 30 days; falls back
// to CollectedAt (and ultimately to now via formatRFC3339).
func reanimatorCollectedAt(result *models.AnalysisResult) time.Time {
	inv := result.InventoryLastModifiedAt
	if !inv.IsZero() && time.Since(inv) <= 30*24*time.Hour {
		return inv
	}
	return result.CollectedAt
}

// formatRFC3339 formats time in RFC3339 format, returns current time if zero
func formatRFC3339(t time.Time) string {
	if t.IsZero() {
		return time.Now().UTC().Format(time.RFC3339)
	}
	return t.UTC().Format(time.RFC3339)
}

// convertBoard converts BoardInfo to Reanimator format
func convertBoard(board models.BoardInfo) ReanimatorBoard {
	return ReanimatorBoard{
		Manufacturer: normalizeNullableString(board.Manufacturer),
		ProductName:  normalizeNullableString(board.ProductName),
		SerialNumber: board.SerialNumber,
		PartNumber:   board.PartNumber,
		UUID:         board.UUID,
	}
}

func canonicalDevicesForExport(hw *models.HardwareConfig) []models.HardwareDevice {
	if hw == nil {
		return nil
	}
	merged := append([]models.HardwareDevice{}, hw.Devices...)
	merged = append(merged, buildDevicesFromLegacy(hw)...)
	hw.Devices = dedupeCanonicalDevices(merged)
	return hw.Devices
}

func buildDevicesFromLegacy(hw *models.HardwareConfig) []models.HardwareDevice {
	if hw == nil {
		return nil
	}
	all := make([]models.HardwareDevice, 0, len(hw.CPUs)+len(hw.Memory)+len(hw.Storage)+len(hw.PCIeDevices)+len(hw.GPUs)+len(hw.NetworkAdapters)+len(hw.PowerSupply))
	nvswitchFirmwareBySlot := buildNVSwitchFirmwareBySlot(hw.Firmware)
	appendDevice := func(d models.HardwareDevice) {
		all = append(all, d)
	}

	for _, cpu := range hw.CPUs {
		details := mergeDetailMaps(nil, cpu.Details)
		details = mergeDetailMaps(details, map[string]any{
			"socket": cpu.Socket,
		})
		appendDevice(models.HardwareDevice{
			Kind:             models.DeviceKindCPU,
			Slot:             fmt.Sprintf("CPU%d", cpu.Socket),
			Model:            cpu.Model,
			SerialNumber:     cpu.SerialNumber,
			Cores:            cpu.Cores,
			Threads:          cpu.Threads,
			FrequencyMHz:     cpu.FrequencyMHz,
			MaxFreqMHz:       cpu.MaxFreqMHz,
			Status:           cpu.Status,
			StatusCheckedAt:  cpu.StatusCheckedAt,
			StatusChangedAt:  cpu.StatusChangedAt,
			StatusAtCollect:  cpu.StatusAtCollect,
			StatusHistory:    cpu.StatusHistory,
			ErrorDescription: cpu.ErrorDescription,
			Details:          details,
		})
	}
	for _, mem := range hw.Memory {
		present := mem.Present
		details := mergeDetailMaps(nil, mem.Details)
		details = mergeDetailMaps(details, map[string]any{
			"max_speed_mhz":     mem.MaxSpeedMHz,
			"current_speed_mhz": mem.CurrentSpeedMHz,
		})
		appendDevice(models.HardwareDevice{
			Kind:             models.DeviceKindMemory,
			Slot:             mem.Slot,
			Location:         mem.Location,
			Manufacturer:     mem.Manufacturer,
			SerialNumber:     mem.SerialNumber,
			PartNumber:       mem.PartNumber,
			Type:             mem.Type,
			Present:          &present,
			SizeMB:           mem.SizeMB,
			Status:           mem.Status,
			StatusCheckedAt:  mem.StatusCheckedAt,
			StatusChangedAt:  mem.StatusChangedAt,
			StatusAtCollect:  mem.StatusAtCollect,
			StatusHistory:    mem.StatusHistory,
			ErrorDescription: mem.ErrorDescription,
			Details:          details,
		})
	}
	for _, stor := range hw.Storage {
		present := stor.Present
		appendDevice(models.HardwareDevice{
			Kind:                  models.DeviceKindStorage,
			Slot:                  stor.Slot,
			Model:                 stor.Model,
			Manufacturer:          stor.Manufacturer,
			RemainingEndurancePct: stor.RemainingEndurancePct,
			SerialNumber:          stor.SerialNumber,
			Firmware:              stor.Firmware,
			Type:                  stor.Type,
			Interface:             stor.Interface,
			Present:               &present,
			SizeGB:                stor.SizeGB,
			Status:                stor.Status,
			StatusCheckedAt:       stor.StatusCheckedAt,
			StatusChangedAt:       stor.StatusChangedAt,
			StatusAtCollect:       stor.StatusAtCollect,
			StatusHistory:         stor.StatusHistory,
			ErrorDescription:      stor.ErrorDescription,
			Details:               mergeDetailMaps(nil, stor.Details),
		})
	}
	for _, pcie := range hw.PCIeDevices {
		// Use PartNumber as model when available; fall back to chip description.
		// Description contains the chip/product name (e.g. "BCM57414 NetXtreme-E …")
		// while PartNumber is a part/product code. Prefer PartNumber when set.
		pcieModel := pcie.PartNumber
		if pcieModel == "" {
			pcieModel = pcie.Description
		}
		details := mergeDetailMaps(nil, pcie.Details)
		pcieFirmware := stringFromDetailMap(details, "firmware")
		if pcieFirmware == "" && isNVSwitchPCIeDevice(pcie) {
			pcieFirmware = nvswitchFirmwareBySlot[normalizeNVSwitchSlotForLookup(pcie.Slot)]
			if pcieFirmware != "" {
				details = mergeDetailMaps(details, map[string]any{
					"firmware": pcieFirmware,
				})
			}
		}
		appendDevice(models.HardwareDevice{
			Kind:             models.DeviceKindPCIe,
			Slot:             pcie.Slot,
			BDF:              pcie.BDF,
			DeviceClass:      pcie.DeviceClass,
			VendorID:         pcie.VendorID,
			DeviceID:         pcie.DeviceID,
			Model:            pcieModel,
			PartNumber:       pcie.PartNumber,
			Manufacturer:     pcie.Manufacturer,
			SerialNumber:     pcie.SerialNumber,
			LinkWidth:        pcie.LinkWidth,
			LinkSpeed:        pcie.LinkSpeed,
			MaxLinkWidth:     pcie.MaxLinkWidth,
			MaxLinkSpeed:     pcie.MaxLinkSpeed,
			NUMANode:         pcie.NUMANode,
			Status:           pcie.Status,
			StatusCheckedAt:  pcie.StatusCheckedAt,
			StatusChangedAt:  pcie.StatusChangedAt,
			StatusAtCollect:  pcie.StatusAtCollect,
			StatusHistory:    pcie.StatusHistory,
			ErrorDescription: pcie.ErrorDescription,
			Details:          details,
		})
	}
	for _, gpu := range hw.GPUs {
		details := mergeDetailMaps(nil, gpu.Details)
		details = mergeDetailMaps(details, map[string]any{
			"uuid":            gpu.UUID,
			"video_bios":      gpu.VideoBIOS,
			"irq":             gpu.IRQ,
			"bus_type":        gpu.BusType,
			"dma_size":        gpu.DMASize,
			"dma_mask":        gpu.DMAMask,
			"device_minor":    gpu.DeviceMinor,
			"temperature":     gpu.Temperature,
			"mem_temperature": gpu.MemTemperature,
			"power":           gpu.Power,
			"max_power":       gpu.MaxPower,
			"clock_speed":     gpu.ClockSpeed,
		})
		appendDevice(models.HardwareDevice{
			Kind:             models.DeviceKindGPU,
			Slot:             gpu.Slot,
			Location:         gpu.Location,
			BDF:              gpu.BDF,
			DeviceClass:      "DisplayController",
			VendorID:         gpu.VendorID,
			DeviceID:         gpu.DeviceID,
			Model:            gpu.Model,
			PartNumber:       gpu.PartNumber,
			Manufacturer:     gpu.Manufacturer,
			SerialNumber:     gpu.SerialNumber,
			Firmware:         gpu.Firmware,
			LinkWidth:        gpu.CurrentLinkWidth,
			LinkSpeed:        gpu.CurrentLinkSpeed,
			MaxLinkWidth:     gpu.MaxLinkWidth,
			MaxLinkSpeed:     gpu.MaxLinkSpeed,
			TemperatureC:     gpu.Temperature,
			Status:           gpu.Status,
			StatusCheckedAt:  gpu.StatusCheckedAt,
			StatusChangedAt:  gpu.StatusChangedAt,
			StatusAtCollect:  gpu.StatusAtCollect,
			StatusHistory:    gpu.StatusHistory,
			ErrorDescription: gpu.ErrorDescription,
			Details:          details,
		})
	}
	for _, nic := range hw.NetworkAdapters {
		present := nic.Present
		appendDevice(models.HardwareDevice{
			Kind:             models.DeviceKindNetwork,
			Slot:             nic.Slot,
			Location:         nic.Location,
			BDF:              nic.BDF,
			VendorID:         nic.VendorID,
			DeviceID:         nic.DeviceID,
			Model:            nic.Model,
			PartNumber:       nic.PartNumber,
			Manufacturer:     nic.Vendor,
			SerialNumber:     nic.SerialNumber,
			Firmware:         nic.Firmware,
			PortCount:        nic.PortCount,
			PortType:         nic.PortType,
			MACAddresses:     nic.MACAddresses,
			LinkWidth:        nic.LinkWidth,
			LinkSpeed:        nic.LinkSpeed,
			MaxLinkWidth:     nic.MaxLinkWidth,
			MaxLinkSpeed:     nic.MaxLinkSpeed,
			NUMANode:         nic.NUMANode,
			Present:          &present,
			Status:           nic.Status,
			StatusCheckedAt:  nic.StatusCheckedAt,
			StatusChangedAt:  nic.StatusChangedAt,
			StatusAtCollect:  nic.StatusAtCollect,
			StatusHistory:    nic.StatusHistory,
			ErrorDescription: nic.ErrorDescription,
			Details:          mergeDetailMaps(nil, nic.Details),
		})
	}
	for _, psu := range hw.PowerSupply {
		present := psu.Present
		appendDevice(models.HardwareDevice{
			Kind:             models.DeviceKindPSU,
			Slot:             psu.Slot,
			Model:            psu.Model,
			PartNumber:       psu.PartNumber,
			Manufacturer:     psu.Vendor,
			SerialNumber:     psu.SerialNumber,
			Firmware:         psu.Firmware,
			Present:          &present,
			WattageW:         psu.WattageW,
			InputType:        psu.InputType,
			InputPowerW:      psu.InputPowerW,
			OutputPowerW:     psu.OutputPowerW,
			InputVoltage:     psu.InputVoltage,
			TemperatureC:     psu.TemperatureC,
			Status:           psu.Status,
			StatusCheckedAt:  psu.StatusCheckedAt,
			StatusChangedAt:  psu.StatusChangedAt,
			StatusAtCollect:  psu.StatusAtCollect,
			StatusHistory:    psu.StatusHistory,
			ErrorDescription: psu.ErrorDescription,
			Details:          mergeDetailMaps(nil, psu.Details),
		})
	}
	return dedupeCanonicalDevices(all)
}

func dedupeCanonicalDevices(items []models.HardwareDevice) []models.HardwareDevice {
	type scored struct {
		item  models.HardwareDevice
		score int
	}
	byKey := make(map[string]scored, len(items))
	order := make([]string, 0, len(items))
	noKey := make([]models.HardwareDevice, 0)
	for _, item := range items {
		key := canonicalKey(item)
		if key == "" {
			noKey = append(noKey, item)
			continue
		}
		curr := scored{item: item, score: canonicalScore(item)}
		prev, ok := byKey[key]
		if !ok {
			byKey[key] = curr
			order = append(order, key)
			continue
		}
		if curr.score > prev.score {
			curr.item = mergeCanonicalDevice(curr.item, prev.item)
			curr.score = canonicalScore(curr.item)
			byKey[key] = curr
			continue
		}
		prev.item = mergeCanonicalDevice(prev.item, curr.item)
		prev.score = canonicalScore(prev.item)
		byKey[key] = prev
	}
	// Secondary pass: for PCIe-class items without serial/BDF (noKey), try to merge
	// into an existing keyed entry with the same model+manufacturer. This handles
	// the case where a device appears both in PCIeDevices (with BDF) and
	// NetworkAdapters (without BDF) — e.g. Inspur outboardPCIeCard vs PCIeCard
	// with the same model. Do not apply this to storage: repeated NVMe slots often
	// share the same model string and would collapse incorrectly.
	// deviceIdentity returns the best available model name for secondary matching,
	// preferring Model over DeviceClass (which may hold a resolved device name).
	deviceIdentity := func(d models.HardwareDevice) string {
		if m := strings.ToLower(strings.TrimSpace(d.Model)); m != "" {
			return m
		}
		if dc := strings.ToLower(strings.TrimSpace(d.DeviceClass)); dc != "" && !isGenericDeviceClass(dc) {
			return dc
		}
		return ""
	}

	var unmatched []models.HardwareDevice
	for _, item := range noKey {
		mergeKind := canonicalMergeKind(item.Kind)
		if mergeKind != "pcie-class" {
			unmatched = append(unmatched, item)
			continue
		}
		identity := deviceIdentity(item)
		mfr := strings.ToLower(strings.TrimSpace(item.Manufacturer))
		if identity == "" {
			unmatched = append(unmatched, item)
			continue
		}
		matchKey := ""
		matchCount := 0
		for _, k := range order {
			existing := byKey[k].item
			if canonicalMergeKind(existing.Kind) == mergeKind &&
				deviceIdentity(existing) == identity &&
				strings.ToLower(strings.TrimSpace(existing.Manufacturer)) == mfr {
				matchKey = k
				matchCount++
			}
		}
		if matchCount == 1 {
			prev := byKey[matchKey]
			prev.item = mergeCanonicalDevice(prev.item, item)
			prev.score = canonicalScore(prev.item)
			byKey[matchKey] = prev
		} else {
			unmatched = append(unmatched, item)
		}
	}

	unmatched = dedupeLooseCanonicalDevices(unmatched)

	out := make([]models.HardwareDevice, 0, len(order)+len(unmatched))
	for _, key := range order {
		out = append(out, byKey[key].item)
	}
	out = append(out, unmatched...)
	for i := range out {
		out[i].ID = out[i].Kind + ":" + strconv.Itoa(i)
	}
	return out
}

func dedupeLooseCanonicalDevices(items []models.HardwareDevice) []models.HardwareDevice {
	if len(items) <= 1 {
		return items
	}
	out := make([]models.HardwareDevice, 0, len(items))
	seen := make(map[string]int, len(items))
	for _, item := range items {
		key := canonicalLooseKey(item)
		if key == "" {
			out = append(out, item)
			continue
		}
		if idx, ok := seen[key]; ok {
			out[idx] = mergeCanonicalDevice(out[idx], item)
			continue
		}
		seen[key] = len(out)
		out = append(out, item)
	}
	return out
}

func mergeCanonicalDevice(primary, secondary models.HardwareDevice) models.HardwareDevice {
	fillString := func(dst *string, src string) {
		if strings.TrimSpace(*dst) == "" && strings.TrimSpace(src) != "" {
			*dst = src
		}
	}
	fillInt := func(dst *int, src int) {
		if *dst == 0 && src != 0 {
			*dst = src
		}
	}
	fillFloat := func(dst *float64, src float64) {
		if *dst == 0 && src != 0 {
			*dst = src
		}
	}

	fillString(&primary.Kind, secondary.Kind)
	fillString(&primary.Source, secondary.Source)
	fillString(&primary.Slot, secondary.Slot)
	fillString(&primary.Location, secondary.Location)
	fillString(&primary.BDF, secondary.BDF)
	fillString(&primary.DeviceClass, secondary.DeviceClass)
	fillInt(&primary.VendorID, secondary.VendorID)
	fillInt(&primary.DeviceID, secondary.DeviceID)
	fillString(&primary.Model, secondary.Model)
	fillString(&primary.PartNumber, secondary.PartNumber)
	fillString(&primary.Manufacturer, secondary.Manufacturer)
	fillString(&primary.SerialNumber, secondary.SerialNumber)
	fillString(&primary.Firmware, secondary.Firmware)
	fillString(&primary.Type, secondary.Type)
	fillString(&primary.Interface, secondary.Interface)
	if primary.Present == nil && secondary.Present != nil {
		primary.Present = secondary.Present
	}
	fillInt(&primary.SizeMB, secondary.SizeMB)
	fillInt(&primary.SizeGB, secondary.SizeGB)
	fillInt(&primary.Cores, secondary.Cores)
	fillInt(&primary.Threads, secondary.Threads)
	fillInt(&primary.FrequencyMHz, secondary.FrequencyMHz)
	fillInt(&primary.MaxFreqMHz, secondary.MaxFreqMHz)
	fillInt(&primary.PortCount, secondary.PortCount)
	fillString(&primary.PortType, secondary.PortType)
	if len(primary.MACAddresses) == 0 && len(secondary.MACAddresses) > 0 {
		primary.MACAddresses = secondary.MACAddresses
	}
	if primary.RemainingEndurancePct == nil && secondary.RemainingEndurancePct != nil {
		primary.RemainingEndurancePct = secondary.RemainingEndurancePct
	}
	fillInt(&primary.LinkWidth, secondary.LinkWidth)
	fillString(&primary.LinkSpeed, secondary.LinkSpeed)
	fillInt(&primary.MaxLinkWidth, secondary.MaxLinkWidth)
	fillString(&primary.MaxLinkSpeed, secondary.MaxLinkSpeed)
	fillInt(&primary.WattageW, secondary.WattageW)
	fillString(&primary.InputType, secondary.InputType)
	fillInt(&primary.InputPowerW, secondary.InputPowerW)
	fillInt(&primary.OutputPowerW, secondary.OutputPowerW)
	fillFloat(&primary.InputVoltage, secondary.InputVoltage)
	fillInt(&primary.TemperatureC, secondary.TemperatureC)
	fillString(&primary.Status, secondary.Status)
	if primary.StatusCheckedAt == nil && secondary.StatusCheckedAt != nil {
		primary.StatusCheckedAt = secondary.StatusCheckedAt
	}
	if primary.StatusChangedAt == nil && secondary.StatusChangedAt != nil {
		primary.StatusChangedAt = secondary.StatusChangedAt
	}
	if primary.StatusAtCollect == nil && secondary.StatusAtCollect != nil {
		primary.StatusAtCollect = secondary.StatusAtCollect
	}
	if len(primary.StatusHistory) == 0 && len(secondary.StatusHistory) > 0 {
		primary.StatusHistory = secondary.StatusHistory
	}
	fillString(&primary.ErrorDescription, secondary.ErrorDescription)
	primary.Details = mergeDetailMaps(primary.Details, secondary.Details)
	return primary
}

func mergeDetailMaps(primary, secondary map[string]any) map[string]any {
	if len(secondary) == 0 {
		return primary
	}
	if primary == nil {
		primary = make(map[string]any, len(secondary))
	}
	for k, v := range secondary {
		if _, exists := primary[k]; !exists {
			primary[k] = v
		}
	}
	return primary
}

func canonicalKey(item models.HardwareDevice) string {
	kind := canonicalMergeKind(item.Kind)
	if sn := normalizedSerial(item.SerialNumber); sn != "" {
		return kind + "|sn:" + strings.ToLower(sn)
	}
	if bdf := strings.ToLower(strings.TrimSpace(item.BDF)); bdf != "" {
		return kind + "|bdf:" + bdf
	}
	return ""
}

func canonicalLooseKey(item models.HardwareDevice) string {
	kind := canonicalMergeKind(item.Kind)
	slot := strings.ToLower(strings.TrimSpace(item.Slot))
	model := strings.ToLower(strings.TrimSpace(item.Model))
	part := strings.ToLower(strings.TrimSpace(item.PartNumber))
	mfr := strings.ToLower(strings.TrimSpace(item.Manufacturer))
	if item.VendorID != 0 && item.DeviceID != 0 && slot != "" {
		return fmt.Sprintf("%s|slotid:%s|%d|%d", kind, slot, item.VendorID, item.DeviceID)
	}
	if slot != "" && model != "" && mfr != "" {
		return kind + "|slotmodel:" + slot + "|" + model + "|" + mfr
	}
	if slot != "" && part != "" && mfr != "" {
		return kind + "|slotpart:" + slot + "|" + part + "|" + mfr
	}
	return ""
}

func canonicalMergeKind(kind string) string {
	switch kind {
	case models.DeviceKindPCIe, models.DeviceKindGPU, models.DeviceKindNetwork:
		return "pcie-class"
	default:
		return strings.TrimSpace(kind)
	}
}

func canonicalScore(item models.HardwareDevice) int {
	score := 0
	if normalizedSerial(item.SerialNumber) != "" {
		score += 6
	}
	if strings.TrimSpace(item.BDF) != "" {
		score += 4
	}
	if strings.TrimSpace(item.Model) != "" {
		score += 3
	}
	if strings.TrimSpace(item.Firmware) != "" {
		score += 2
	}
	if strings.TrimSpace(item.Status) != "" {
		score++
	}
	return score
}

// convertFirmware converts firmware information to Reanimator format
func convertFirmware(firmware []models.FirmwareInfo) []ReanimatorFirmware {
	if len(firmware) == 0 {
		return nil
	}

	result := make([]ReanimatorFirmware, 0, len(firmware))
	for _, fw := range firmware {
		if isDeviceBoundFirmwareName(fw.DeviceName) || isDeviceBoundFirmwareFQDD(fw.Description) {
			continue
		}
		result = append(result, ReanimatorFirmware{
			DeviceName: fw.DeviceName,
			Version:    fw.Version,
		})
	}
	if len(result) == 0 {
		return nil
	}
	return result
}

func convertCPUsFromDevices(devices []models.HardwareDevice, collectedAt, boardSerial string, microcodeBySocket map[int]string) []ReanimatorCPU {
	result := make([]ReanimatorCPU, 0)
	for _, d := range devices {
		if d.Kind != models.DeviceKindCPU {
			continue
		}
		if d.Present != nil && !*d.Present {
			continue
		}
		socket := parseSocketFromSlot(d.Slot)
		if v, ok := d.Details["socket"].(int); ok {
			socket = v
		}
		cpuStatus := normalizeStatus(d.Status, false)
		if strings.TrimSpace(d.Status) == "" {
			cpuStatus = "Unknown"
		}
		meta := buildStatusMeta(cpuStatus, d.StatusCheckedAt, d.StatusChangedAt, d.StatusHistory, d.ErrorDescription, collectedAt)
		result = append(result, ReanimatorCPU{
			Socket:                  socket,
			Model:                   d.Model,
			Cores:                   d.Cores,
			Threads:                 d.Threads,
			FrequencyMHz:            d.FrequencyMHz,
			MaxFrequencyMHz:         d.MaxFreqMHz,
			TemperatureC:            floatFromDetailMap(d.Details, "temperature_c"),
			PowerW:                  floatFromDetailMap(d.Details, "power_w"),
			Throttled:               boolPtrFromDetailMap(d.Details, "throttled"),
			CorrectableErrorCount:   int64FromDetailMap(d.Details, "correctable_error_count"),
			UncorrectableErrorCount: int64FromDetailMap(d.Details, "uncorrectable_error_count"),
			LifeRemainingPct:        floatFromDetailMap(d.Details, "life_remaining_pct"),
			LifeUsedPct:             floatFromDetailMap(d.Details, "life_used_pct"),
			SerialNumber:            strings.TrimSpace(d.SerialNumber),
			Firmware: firstNonEmptyString(
				stringFromDetailMap(d.Details, "microcode"),
				microcodeBySocket[socket],
				stringFromDetailMap(d.Details, "firmware"),
			),
			Manufacturer:         inferCPUManufacturer(d.Model),
			Status:               cpuStatus,
			StatusCheckedAt:      meta.StatusCheckedAt,
			StatusChangedAt:      meta.StatusChangedAt,
			ManufacturedYearWeek: manufacturedYearWeekFromDetails(d.Details),
			StatusHistory:        meta.StatusHistory,
			ErrorDescription:     meta.ErrorDescription,
		})
	}
	return result
}

func convertMemoryFromDevices(devices []models.HardwareDevice, collectedAt string) []ReanimatorMemory {
	result := make([]ReanimatorMemory, 0)
	for _, d := range devices {
		if d.Kind != models.DeviceKindMemory {
			continue
		}
		present := boolFromPresentPtr(d.Present, true)
		status := normalizeStatus(d.Status, true)
		mem := models.MemoryDIMM{
			Present:      present,
			SizeMB:       d.SizeMB,
			Type:         d.Type,
			Description:  stringFromDetailMap(d.Details, "description"),
			Manufacturer: d.Manufacturer,
			SerialNumber: d.SerialNumber,
			PartNumber:   d.PartNumber,
			Status:       d.Status,
		}
		if !mem.IsInstalledInventory() || status == "Empty" || strings.TrimSpace(d.SerialNumber) == "" {
			continue
		}
		meta := buildStatusMeta(status, d.StatusCheckedAt, d.StatusChangedAt, d.StatusHistory, d.ErrorDescription, collectedAt)
		result = append(result, ReanimatorMemory{
			Slot:                       d.Slot,
			Location:                   d.Location,
			SizeMB:                     d.SizeMB,
			Type:                       d.Type,
			MaxSpeedMHz:                intFromDetailMap(d.Details, "max_speed_mhz"),
			CurrentSpeedMHz:            intFromDetailMap(d.Details, "current_speed_mhz"),
			TemperatureC:               floatFromDetailMap(d.Details, "temperature_c"),
			CorrectableECCErrorCount:   int64FromDetailMap(d.Details, "correctable_ecc_error_count"),
			UncorrectableECCErrorCount: int64FromDetailMap(d.Details, "uncorrectable_ecc_error_count"),
			LifeRemainingPct:           floatFromDetailMap(d.Details, "life_remaining_pct"),
			LifeUsedPct:                floatFromDetailMap(d.Details, "life_used_pct"),
			SpareBlocksRemainingPct:    floatFromDetailMap(d.Details, "spare_blocks_remaining_pct"),
			PerformanceDegraded:        boolPtrFromDetailMap(d.Details, "performance_degraded"),
			DataLossDetected:           boolPtrFromDetailMap(d.Details, "data_loss_detected"),
			Manufacturer:               d.Manufacturer,
			SerialNumber:               d.SerialNumber,
			PartNumber:                 d.PartNumber,
			Status:                     status,
			StatusCheckedAt:            meta.StatusCheckedAt,
			StatusChangedAt:            meta.StatusChangedAt,
			ManufacturedYearWeek:       manufacturedYearWeekFromDetails(d.Details),
			StatusHistory:              meta.StatusHistory,
			ErrorDescription:           meta.ErrorDescription,
		})
	}
	return result
}

func convertStorageFromDevices(devices []models.HardwareDevice, collectedAt string) []ReanimatorStorage {
	result := make([]ReanimatorStorage, 0)
	for _, d := range devices {
		if d.Kind != models.DeviceKindStorage {
			continue
		}
		if isVirtualExportStorageDevice(d) {
			continue
		}
		if !shouldExportStorageDevice(d) {
			continue
		}
		present := boolFromPresentPtr(d.Present, true)
		status := inferStorageStatus(models.Storage{Present: present})
		if strings.TrimSpace(d.Status) != "" {
			status = normalizeStatus(d.Status, !present)
		}
		meta := buildStatusMeta(status, d.StatusCheckedAt, d.StatusChangedAt, d.StatusHistory, d.ErrorDescription, collectedAt)
		presentValue := present
		result = append(result, ReanimatorStorage{
			Slot:                  d.Slot,
			Type:                  d.Type,
			Model:                 d.Model,
			SizeGB:                d.SizeGB,
			SerialNumber:          d.SerialNumber,
			Manufacturer:          d.Manufacturer,
			Firmware:              d.Firmware,
			Interface:             d.Interface,
			Present:               &presentValue,
			TemperatureC:          floatFromDetailMap(d.Details, "temperature_c"),
			PowerOnHours:          int64FromDetailMap(d.Details, "power_on_hours"),
			PowerCycles:           int64FromDetailMap(d.Details, "power_cycles"),
			UnsafeShutdowns:       int64FromDetailMap(d.Details, "unsafe_shutdowns"),
			MediaErrors:           int64FromDetailMap(d.Details, "media_errors"),
			ErrorLogEntries:       int64FromDetailMap(d.Details, "error_log_entries"),
			WrittenBytes:          int64FromDetailMap(d.Details, "written_bytes"),
			ReadBytes:             int64FromDetailMap(d.Details, "read_bytes"),
			LifeUsedPct:           floatFromDetailMap(d.Details, "life_used_pct"),
			RemainingEndurancePct: d.RemainingEndurancePct,
			LifeRemainingPct:      floatFromDetailMap(d.Details, "life_remaining_pct"),
			AvailableSparePct:     floatFromDetailMap(d.Details, "available_spare_pct"),
			ReallocatedSectors:    int64FromDetailMap(d.Details, "reallocated_sectors"),
			CurrentPendingSectors: int64FromDetailMap(d.Details, "current_pending_sectors"),
			OfflineUncorrectable:  int64FromDetailMap(d.Details, "offline_uncorrectable"),
			Status:                status,
			StatusCheckedAt:       meta.StatusCheckedAt,
			StatusChangedAt:       meta.StatusChangedAt,
			ManufacturedYearWeek:  manufacturedYearWeekFromDetails(d.Details),
			StatusHistory:         meta.StatusHistory,
			ErrorDescription:      meta.ErrorDescription,
		})
	}
	return result
}

func convertPCIeFromDevices(devices []models.HardwareDevice, collectedAt string) []ReanimatorPCIe {
	result := make([]ReanimatorPCIe, 0)
	for _, d := range devices {
		if d.Kind != models.DeviceKindPCIe && d.Kind != models.DeviceKindGPU && d.Kind != models.DeviceKindNetwork {
			continue
		}
		if isStorageEndpointPCIeDevice(d) {
			continue
		}
		if isPlaceholderPCIeExportDevice(d) {
			continue
		}
		if d.Present != nil && !*d.Present {
			continue
		}
		deviceClass := normalizePCIeDeviceClass(d)
		model := normalizePlaceholderDeviceModel(d.Model)
		if model == "" {
			model = normalizePlaceholderDeviceModel(d.PartNumber)
		}
		// General rule: if model not found in source data but PCI IDs are known, resolve from pci.ids.
		if model == "" && d.VendorID != 0 && d.DeviceID != 0 {
			model = pciids.DeviceName(d.VendorID, d.DeviceID)
		}
		manufacturer := d.Manufacturer
		if manufacturer == "" && d.VendorID != 0 {
			manufacturer = pciids.VendorName(d.VendorID)
		}
		temperatureC := firstNonZeroFloat(
			float64(d.TemperatureC),
			floatFromDetailMap(d.Details, "temperature_c"),
			floatFromDetailMap(d.Details, "temperature"),
		)
		powerW := firstNonZeroFloat(
			floatFromDetailMap(d.Details, "power_w"),
			float64(intFromDetailMap(d.Details, "power")),
		)
		status := normalizeStatus(d.Status, false)
		meta := buildStatusMeta(status, d.StatusCheckedAt, d.StatusChangedAt, d.StatusHistory, d.ErrorDescription, collectedAt)
		slot := firstNonEmptyString(d.Slot, d.BDF)
		result = append(result, ReanimatorPCIe{
			Slot:                   slot,
			VendorID:               d.VendorID,
			DeviceID:               d.DeviceID,
			NUMANode:               d.NUMANode,
			TemperatureC:           temperatureC,
			PowerW:                 powerW,
			LifeRemainingPct:       floatFromDetailMap(d.Details, "life_remaining_pct"),
			LifeUsedPct:            floatFromDetailMap(d.Details, "life_used_pct"),
			ECCCorrectedTotal:      int64FromDetailMap(d.Details, "ecc_corrected_total"),
			ECCUncorrectedTotal:    int64FromDetailMap(d.Details, "ecc_uncorrected_total"),
			HWSlowdown:             boolPtrFromDetailMap(d.Details, "hw_slowdown"),
			BatteryChargePct:       floatFromDetailMap(d.Details, "battery_charge_pct"),
			BatteryHealthPct:       floatFromDetailMap(d.Details, "battery_health_pct"),
			BatteryTemperatureC:    floatFromDetailMap(d.Details, "battery_temperature_c"),
			BatteryVoltageV:        floatFromDetailMap(d.Details, "battery_voltage_v"),
			BatteryReplaceRequired: boolPtrFromDetailMap(d.Details, "battery_replace_required"),
			SFPTemperatureC:        floatFromDetailMap(d.Details, "sfp_temperature_c"),
			SFPTXPowerDBm:          floatFromDetailMap(d.Details, "sfp_tx_power_dbm"),
			SFPRXPowerDBm:          floatFromDetailMap(d.Details, "sfp_rx_power_dbm"),
			SFPVoltageV:            floatFromDetailMap(d.Details, "sfp_voltage_v"),
			SFPBiasMA:              floatFromDetailMap(d.Details, "sfp_bias_ma"),
			BDF:                    d.BDF,
			DeviceClass:            deviceClass,
			Manufacturer:           manufacturer,
			Model:                  model,
			LinkWidth:              d.LinkWidth,
			LinkSpeed:              d.LinkSpeed,
			MaxLinkWidth:           d.MaxLinkWidth,
			MaxLinkSpeed:           d.MaxLinkSpeed,
			MACAddresses:           append([]string(nil), d.MACAddresses...),
			SerialNumber:           strings.TrimSpace(d.SerialNumber),
			Firmware:               firstNonEmptyString(d.Firmware, stringFromDetailMap(d.Details, "firmware")),
			Status:                 status,
			StatusCheckedAt:        meta.StatusCheckedAt,
			StatusChangedAt:        meta.StatusChangedAt,
			ManufacturedYearWeek:   manufacturedYearWeekFromDetails(d.Details),
			StatusHistory:          meta.StatusHistory,
			ErrorDescription:       meta.ErrorDescription,
		})
	}
	return result
}

func isStorageEndpointPCIeDevice(d models.HardwareDevice) bool {
	if d.Kind != models.DeviceKindPCIe {
		return false
	}

	class := strings.ToLower(strings.TrimSpace(d.DeviceClass))
	if !strings.Contains(class, "storage") && !strings.Contains(class, "nonvolatile") && !strings.Contains(class, "nvme") {
		return false
	}

	joined := strings.ToLower(strings.TrimSpace(strings.Join([]string{
		d.Slot,
		d.Model,
		d.PartNumber,
		d.Manufacturer,
		stringFromDetailMap(d.Details, "description"),
	}, " ")))

	if strings.Contains(joined, "raid") || strings.Contains(joined, "hba") || strings.Contains(joined, "controller") {
		return false
	}

	return strings.Contains(joined, "nvme") ||
		strings.Contains(joined, "ssd") ||
		strings.Contains(joined, "u.2") ||
		strings.Contains(joined, "e1.s") ||
		strings.Contains(joined, "e3.s") ||
		strings.Contains(joined, "disk") ||
		strings.Contains(joined, "drive")
}

func isVirtualExportStorageDevice(d models.HardwareDevice) bool {
	if d.Kind != models.DeviceKindStorage {
		return false
	}
	mfr := strings.ToUpper(strings.TrimSpace(d.Manufacturer))
	model := strings.ToUpper(strings.TrimSpace(d.Model))
	slot := strings.ToUpper(strings.TrimSpace(d.Slot))
	if strings.Contains(mfr, "AMERICAN MEGATRENDS") || strings.Contains(mfr, "AMI") {
		joined := strings.Join([]string{mfr, model, slot}, " ")
		for _, marker := range []string{
			"VIRTUAL CDROM",
			"VIRTUAL CD/DVD",
			"VIRTUAL FLOPPY",
			"VIRTUAL FDD",
			"VIRTUAL MEDIA",
			"USB_DEVICE",
		} {
			if strings.Contains(joined, marker) {
				return true
			}
		}
	}
	return false
}

func isPlaceholderPCIeExportDevice(d models.HardwareDevice) bool {
	if d.Kind != models.DeviceKindPCIe && d.Kind != models.DeviceKindNetwork {
		return false
	}
	if strings.TrimSpace(d.BDF) != "" {
		return false
	}
	if d.VendorID != 0 || d.DeviceID != 0 {
		return false
	}
	if normalizedSerial(d.SerialNumber) != "" {
		return false
	}
	if len(d.MACAddresses) > 0 {
		return false
	}
	if strings.TrimSpace(d.Firmware) != "" {
		return false
	}
	if d.LinkWidth != 0 || d.MaxLinkWidth != 0 || strings.TrimSpace(d.LinkSpeed) != "" || strings.TrimSpace(d.MaxLinkSpeed) != "" {
		return false
	}
	if hasMeaningfulExporterText(d.Model) || hasMeaningfulExporterText(d.PartNumber) || hasMeaningfulExporterText(d.Manufacturer) || hasMeaningfulExporterText(stringFromDetailMap(d.Details, "description")) {
		return false
	}

	class := strings.ToLower(strings.TrimSpace(d.DeviceClass))
	if class != "" && class != "unknown" && class != "other" && class != "pcie device" && class != "network" && class != "network controller" && class != "networkcontroller" {
		return false
	}

	return isNumericExporterSlot(d.Slot)
}

func convertPSUsFromDevices(devices []models.HardwareDevice, collectedAt string) []ReanimatorPSU {
	result := make([]ReanimatorPSU, 0)
	for _, d := range devices {
		if d.Kind != models.DeviceKindPSU {
			continue
		}
		present := d.Present != nil && *d.Present
		if !present || strings.TrimSpace(d.SerialNumber) == "" {
			continue
		}
		status := normalizeStatus(d.Status, false)
		meta := buildStatusMeta(status, d.StatusCheckedAt, d.StatusChangedAt, d.StatusHistory, d.ErrorDescription, collectedAt)
		result = append(result, ReanimatorPSU{
			Slot:                 d.Slot,
			Model:                d.Model,
			Vendor:               d.Manufacturer,
			WattageW:             d.WattageW,
			SerialNumber:         d.SerialNumber,
			PartNumber:           d.PartNumber,
			Firmware:             d.Firmware,
			Status:               status,
			InputType:            d.InputType,
			InputPowerW:          float64(d.InputPowerW),
			OutputPowerW:         float64(d.OutputPowerW),
			InputVoltage:         d.InputVoltage,
			TemperatureC:         firstNonZeroFloat(float64(d.TemperatureC), floatFromDetailMap(d.Details, "temperature_c")),
			LifeRemainingPct:     floatFromDetailMap(d.Details, "life_remaining_pct"),
			LifeUsedPct:          floatFromDetailMap(d.Details, "life_used_pct"),
			StatusCheckedAt:      meta.StatusCheckedAt,
			StatusChangedAt:      meta.StatusChangedAt,
			ManufacturedYearWeek: manufacturedYearWeekFromDetails(d.Details),
			StatusHistory:        meta.StatusHistory,
			ErrorDescription:     meta.ErrorDescription,
		})
	}
	return result
}

func convertEventLogs(events []models.Event, collectedAt string) []ReanimatorEventLog {
	if len(events) == 0 {
		return nil
	}

	out := make([]ReanimatorEventLog, 0, len(events))
	for _, event := range events {
		source := normalizeEventLogSource(event.Source)
		message := strings.TrimSpace(event.Description)
		if source == "" || message == "" {
			continue
		}

		item := ReanimatorEventLog{
			Source:       source,
			EventTime:    formatEventLogTime(event.Timestamp, collectedAt),
			Severity:     normalizeEventLogSeverity(event.Severity),
			MessageID:    strings.TrimSpace(event.ID),
			Message:      message,
			ComponentRef: firstNonEmptyString(strings.TrimSpace(event.SensorName), strings.TrimSpace(event.SensorType)),
		}
		if raw := strings.TrimSpace(event.RawData); raw != "" {
			item.RawPayload = map[string]any{
				"raw_data": raw,
			}
		}
		out = append(out, item)
	}

	if len(out) == 0 {
		return nil
	}
	return out
}

func convertSensors(sensors []models.SensorReading) *ReanimatorSensors {
	if len(sensors) == 0 {
		return nil
	}

	out := &ReanimatorSensors{}
	seenFans := map[string]struct{}{}
	powerIndex := map[string]int{}
	seenTemps := map[string]struct{}{}
	seenOther := map[string]struct{}{}

	for _, s := range sensors {
		name := strings.TrimSpace(s.Name)
		if name == "" {
			continue
		}
		if !sensorHasNumericReading(s) {
			continue
		}
		status := normalizeSensorStatus(s.Status)
		sType := strings.ToLower(strings.TrimSpace(s.Type))
		unit := strings.TrimSpace(s.Unit)

		switch {
		case sType == "fan" || strings.EqualFold(unit, "RPM"):
			if seenFirst(seenFans, name) {
				continue
			}
			out.Fans = append(out.Fans, ReanimatorFanSensor{
				Name:   name,
				RPM:    int(s.Value),
				Status: status,
			})
		case sType == "power" || sType == "voltage" || sType == "current" || strings.EqualFold(unit, "V") || strings.EqualFold(unit, "A") || strings.EqualFold(unit, "W"):
			baseName := groupedPowerSensorName(name)
			if idx, ok := powerIndex[baseName]; ok {
				mergePowerSensorReading(&out.Power[idx], sType, unit, s.Value, status)
				continue
			}
			item := ReanimatorPowerSensor{
				Name:   baseName,
				Status: status,
			}
			mergePowerSensorReading(&item, sType, unit, s.Value, status)
			powerIndex[baseName] = len(out.Power)
			out.Power = append(out.Power, item)
		case sType == "temperature" || strings.EqualFold(unit, "C") || strings.EqualFold(unit, "°C"):
			if seenFirst(seenTemps, name) {
				continue
			}
			out.Temperatures = append(out.Temperatures, ReanimatorTemperatureSensor{
				Name:    name,
				Celsius: s.Value,
				Status:  status,
			})
		default:
			if seenFirst(seenOther, name) {
				continue
			}
			out.Other = append(out.Other, ReanimatorOtherSensor{
				Name:   name,
				Value:  s.Value,
				Unit:   unit,
				Status: status,
			})
		}
	}

	if len(out.Fans) == 0 && len(out.Power) == 0 && len(out.Temperatures) == 0 && len(out.Other) == 0 {
		return nil
	}
	return out
}

func groupedPowerSensorName(name string) string {
	trimmed := strings.TrimSpace(name)
	lower := strings.ToLower(trimmed)
	inputSuffixes := []string{"_inputpower", "_inputvoltage", "_inputcurrent", "_pin", "_vin", "_iin"}
	for _, suffix := range inputSuffixes {
		if strings.HasSuffix(lower, suffix) {
			return strings.TrimSpace(trimmed[:len(trimmed)-len(suffix)])
		}
	}
	outputSuffixes := []string{"_outputpower", "_outputvoltage", "_outputcurrent", "_pout", "_vout", "_iout"}
	for _, suffix := range outputSuffixes {
		if strings.HasSuffix(lower, suffix) {
			return strings.TrimSpace(trimmed[:len(trimmed)-len(suffix)]) + "_Output"
		}
	}
	return trimmed
}

func mergePowerSensorReading(item *ReanimatorPowerSensor, sType, unit string, value float64, status string) {
	if item == nil {
		return
	}
	switch {
	case sType == "current" || strings.EqualFold(unit, "A"):
		item.CurrentA = value
	case sType == "power" || strings.EqualFold(unit, "W"):
		item.PowerW = value
	default:
		item.VoltageV = value
	}
	item.Status = mergeSensorStatus(item.Status, status)
}

func mergeSensorStatus(current, incoming string) string {
	current = strings.TrimSpace(current)
	incoming = strings.TrimSpace(incoming)
	if current == "" {
		return incoming
	}
	if incoming == "" {
		return current
	}
	if sensorStatusRank(incoming) > sensorStatusRank(current) {
		return incoming
	}
	return current
}

func sensorStatusRank(status string) int {
	switch strings.ToLower(strings.TrimSpace(status)) {
	case "critical":
		return 3
	case "warning":
		return 2
	case "ok":
		return 1
	default:
		return 0
	}
}

func sensorHasNumericReading(s models.SensorReading) bool {
	if strings.TrimSpace(s.RawValue) != "" {
		if _, err := strconv.ParseFloat(strings.TrimSpace(s.RawValue), 64); err == nil {
			return true
		}
	}
	return s.Value != 0
}

func isDeviceBoundFirmwareName(name string) bool {
	n := strings.TrimSpace(strings.ToLower(name))
	if n == "" {
		return false
	}

	if strings.HasPrefix(n, "gpu ") ||
		strings.HasPrefix(n, "nvswitch ") ||
		strings.HasPrefix(n, "nic ") ||
		strings.HasPrefix(n, "hdd ") ||
		strings.HasPrefix(n, "ssd ") ||
		strings.HasPrefix(n, "nvme ") ||
		strings.HasPrefix(n, "psu") ||
		// Supermicro Redfish FirmwareInventory names "GPU1 System Slot0", "NIC1 System Slot0 ..."
		// where the number follows immediately after the type prefix (no space separator).
		(strings.HasPrefix(n, "gpu") && len(n) > 3 && n[3] >= '0' && n[3] <= '9') ||
		(strings.HasPrefix(n, "nic") && len(n) > 3 && n[3] >= '0' && n[3] <= '9') ||
		// "NVMeController1" — storage controller bound to an NVMe device slot
		strings.HasPrefix(n, "nvmecontroller") ||
		// "Power supply N" — Supermicro PSU firmware (distinct from generic "PSU" prefix)
		strings.HasPrefix(n, "power supply") ||
		// "Software Inventory" — generic label used by HGX baseboard for all per-component
		// firmware slots (GPU, NVSwitch, PCIeRetimer, ERoT, InfoROM, etc.). The useful name
		// is only in the inventory item Id, not the Name field, so the entry is not actionable.
		n == "software inventory" ||
		// HGX baseboard firmware inventory IDs for device-bound components
		strings.Contains(n, "_fw_gpu_") ||
		strings.Contains(n, "_fw_nvswitch_") ||
		strings.Contains(n, "_fw_erot_") ||
		strings.Contains(n, "_inforom_gpu_") {
		return true
	}

	return cpuMicrocodeFirmwareRegex.MatchString(strings.TrimSpace(name))
}

func normalizeEventLogSource(source string) string {
	switch strings.ToLower(strings.TrimSpace(source)) {
	case "redfish":
		return "redfish"
	case "sel", "bmc", "ipmi", "idrac", "lifecycle controller", "lifecyclecontroller":
		return "bmc"
	case "system", "syslog", "smart", "zfs", "file", "gpu", "dmi", "nvidia driver", "gpu field diagnostics", "fan", "memory", "host":
		return "host"
	default:
		return ""
	}
}

func normalizeEventLogSeverity(severity models.Severity) string {
	switch severity {
	case models.SeverityCritical:
		return "Critical"
	case models.SeverityWarning:
		return "Warning"
	case models.SeverityInfo:
		return "Info"
	default:
		return ""
	}
}

func formatEventLogTime(ts time.Time, collectedAt string) string {
	if !ts.IsZero() {
		return ts.UTC().Format(time.RFC3339)
	}
	return strings.TrimSpace(collectedAt)
}

func manufacturedYearWeekFromDetails(details map[string]any) string {
	if details == nil {
		return ""
	}
	value := normalizeManufacturedYearWeek(stringFromDetailMap(details, "manufactured_year_week"))
	if value != "" {
		return value
	}
	return normalizeManufacturedYearWeek(stringFromDetailMap(details, "mfg_date"))
}

var manufacturedYearWeekRegex = regexp.MustCompile(`^\d{4}-W\d{2}$`)

func normalizeManufacturedYearWeek(value string) string {
	value = strings.TrimSpace(strings.ToUpper(value))
	if manufacturedYearWeekRegex.MatchString(value) {
		return value
	}
	return ""
}

// isDeviceBoundFirmwareFQDD returns true if the description looks like a device-bound FQDD
// (e.g. NIC.Integrated.1-1-1, PSU.Slot.1, Disk.Bay.0:..., RAID.SL.3-1, InfiniBand.Slot.1-1).
// These firmware entries are already embedded in the device itself and must not appear
// in hardware.firmware.
func isDeviceBoundFirmwareFQDD(desc string) bool {
	d := strings.ToLower(strings.TrimSpace(desc))
	if d == "" {
		return false
	}
	// "raid." covers all RAID controller/backplane FQDDs: RAID.SL.*, RAID.Integrated.*, RAID.Backplane.*
	// "infiniband." covers Mellanox InfiniBand/Ethernet adapters exposed as InfiniBand.Slot.*
	for _, prefix := range []string{"nic.", "psu.", "disk.", "raid.", "gpu.", "infiniband.", "fc."} {
		if strings.HasPrefix(d, prefix) {
			return true
		}
	}
	return false
}

func buildCPUMicrocodeBySocket(firmware []models.FirmwareInfo) map[int]string {
	if len(firmware) == 0 {
		return nil
	}
	out := make(map[int]string)
	for _, fw := range firmware {
		m := cpuMicrocodeFirmwareCaptureRegex.FindStringSubmatch(strings.TrimSpace(fw.DeviceName))
		if len(m) != 2 || strings.TrimSpace(fw.Version) == "" {
			continue
		}
		socket, err := strconv.Atoi(m[1])
		if err != nil {
			continue
		}
		if _, exists := out[socket]; exists {
			continue
		}
		out[socket] = strings.TrimSpace(fw.Version)
	}
	if len(out) == 0 {
		return nil
	}
	return out
}

// convertCPUs converts CPU information to Reanimator format
func convertCPUs(cpus []models.CPU, collectedAt string) []ReanimatorCPU {
	if len(cpus) == 0 {
		return nil
	}

	result := make([]ReanimatorCPU, 0, len(cpus))
	for _, cpu := range cpus {
		manufacturer := inferCPUManufacturer(cpu.Model)

		cpuStatus := normalizeStatus(cpu.Status, false)
		if strings.TrimSpace(cpu.Status) == "" {
			cpuStatus = "Unknown"
		}
		meta := buildStatusMeta(
			cpuStatus,
			cpu.StatusCheckedAt,
			cpu.StatusChangedAt,
			cpu.StatusHistory,
			cpu.ErrorDescription,
			collectedAt,
		)

		result = append(result, ReanimatorCPU{
			Socket:           cpu.Socket,
			Model:            cpu.Model,
			Cores:            cpu.Cores,
			Threads:          cpu.Threads,
			FrequencyMHz:     cpu.FrequencyMHz,
			MaxFrequencyMHz:  cpu.MaxFreqMHz,
			SerialNumber:     strings.TrimSpace(cpu.SerialNumber),
			Firmware:         "",
			Manufacturer:     manufacturer,
			Status:           cpuStatus,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}
	return result
}

// convertMemory converts memory modules to Reanimator format
func convertMemory(memory []models.MemoryDIMM, collectedAt string) []ReanimatorMemory {
	if len(memory) == 0 {
		return nil
	}

	result := make([]ReanimatorMemory, 0, len(memory))
	for _, mem := range memory {
		if !mem.IsInstalledInventory() || normalizeStatus(mem.Status, true) == "Empty" || strings.TrimSpace(mem.SerialNumber) == "" {
			continue
		}
		status := normalizeStatus(mem.Status, true)

		meta := buildStatusMeta(
			status,
			mem.StatusCheckedAt,
			mem.StatusChangedAt,
			mem.StatusHistory,
			mem.ErrorDescription,
			collectedAt,
		)

		result = append(result, ReanimatorMemory{
			Slot:             mem.Slot,
			Location:         mem.Location,
			SizeMB:           mem.SizeMB,
			Type:             mem.Type,
			MaxSpeedMHz:      mem.MaxSpeedMHz,
			CurrentSpeedMHz:  mem.CurrentSpeedMHz,
			Manufacturer:     mem.Manufacturer,
			SerialNumber:     mem.SerialNumber,
			PartNumber:       mem.PartNumber,
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}
	return result
}

// convertStorage converts storage devices to Reanimator format
func convertStorage(storage []models.Storage, collectedAt string) []ReanimatorStorage {
	if len(storage) == 0 {
		return nil
	}

	result := make([]ReanimatorStorage, 0, len(storage))
	for _, stor := range storage {
		if isVirtualLegacyStorageDevice(stor) {
			continue
		}
		if !shouldExportLegacyStorage(stor) {
			continue
		}

		status := inferStorageStatus(stor)
		if strings.TrimSpace(stor.Status) != "" {
			status = normalizeStatus(stor.Status, !stor.Present)
		}
		meta := buildStatusMeta(
			status,
			stor.StatusCheckedAt,
			stor.StatusChangedAt,
			stor.StatusHistory,
			stor.ErrorDescription,
			collectedAt,
		)
		present := stor.Present

		result = append(result, ReanimatorStorage{
			Slot:                  stor.Slot,
			Type:                  stor.Type,
			Model:                 stor.Model,
			SizeGB:                stor.SizeGB,
			SerialNumber:          stor.SerialNumber,
			Manufacturer:          stor.Manufacturer,
			Firmware:              stor.Firmware,
			Interface:             stor.Interface,
			Present:               &present,
			RemainingEndurancePct: stor.RemainingEndurancePct,
			Status:                status,
			StatusCheckedAt:       meta.StatusCheckedAt,
			StatusChangedAt:       meta.StatusChangedAt,
			StatusHistory:         meta.StatusHistory,
			ErrorDescription:      meta.ErrorDescription,
		})
	}
	return result
}

func shouldExportStorageDevice(d models.HardwareDevice) bool {
	if normalizedSerial(d.SerialNumber) != "" {
		return true
	}
	if strings.TrimSpace(d.Slot) != "" {
		return true
	}
	if hasMeaningfulExporterText(d.Model) {
		return true
	}
	if hasMeaningfulExporterText(d.Type) || hasMeaningfulExporterText(d.Interface) {
		return true
	}
	if d.SizeGB > 0 {
		return true
	}
	return d.Present != nil
}

func shouldExportLegacyStorage(stor models.Storage) bool {
	if normalizedSerial(stor.SerialNumber) != "" {
		return true
	}
	if strings.TrimSpace(stor.Slot) != "" {
		return true
	}
	if hasMeaningfulExporterText(stor.Model) {
		return true
	}
	if hasMeaningfulExporterText(stor.Type) || hasMeaningfulExporterText(stor.Interface) {
		return true
	}
	if stor.SizeGB > 0 {
		return true
	}
	return stor.Present
}

func isVirtualLegacyStorageDevice(stor models.Storage) bool {
	return isVirtualExportStorageDevice(models.HardwareDevice{
		Kind:         models.DeviceKindStorage,
		Slot:         stor.Slot,
		Model:        stor.Model,
		Manufacturer: stor.Manufacturer,
	})
}

// convertPCIeDevices converts PCIe devices, GPUs, and network adapters to Reanimator format
func convertPCIeDevices(hw *models.HardwareConfig, collectedAt string) []ReanimatorPCIe {
	result := make([]ReanimatorPCIe, 0)
	gpuSlots := make(map[string]struct{}, len(hw.GPUs))
	nvswitchFirmwareBySlot := buildNVSwitchFirmwareBySlot(hw.Firmware)
	for _, gpu := range hw.GPUs {
		slot := strings.ToLower(strings.TrimSpace(gpu.Slot))
		if slot != "" {
			gpuSlots[slot] = struct{}{}
		}
	}

	// Convert regular PCIe devices
	for _, pcie := range hw.PCIeDevices {
		slot := strings.ToLower(strings.TrimSpace(pcie.Slot))
		if _, isDedicatedGPU := gpuSlots[slot]; isDedicatedGPU || isDisplayClass(pcie.DeviceClass) {
			// Skip GPU-like PCIe entries to avoid duplicates:
			// dedicated GPUs are exported from hw.GPUs with richer metadata.
			continue
		}

		if isStorageEndpointPCIeDevice(models.HardwareDevice{
			Kind:         models.DeviceKindPCIe,
			Slot:         pcie.Slot,
			DeviceClass:  pcie.DeviceClass,
			Model:        pcie.Description,
			PartNumber:   pcie.PartNumber,
			Manufacturer: pcie.Manufacturer,
		}) {
			continue
		}

		serialNumber := strings.TrimSpace(pcie.SerialNumber)

		// Determine model: PartNumber > Description (chip name) > DeviceClass (bus width fallback)
		model := normalizePlaceholderDeviceModel(pcie.PartNumber)
		if model == "" {
			model = normalizePlaceholderDeviceModel(pcie.Description)
		}
		if model == "" {
			model = pcie.DeviceClass
		}

		status := normalizeStatus(pcie.Status, false)
		firmware := ""
		if isNVSwitchPCIeDevice(pcie) {
			firmware = nvswitchFirmwareBySlot[normalizeNVSwitchSlotForLookup(pcie.Slot)]
		}
		meta := buildStatusMeta(
			status,
			pcie.StatusCheckedAt,
			pcie.StatusChangedAt,
			pcie.StatusHistory,
			pcie.ErrorDescription,
			collectedAt,
		)

		result = append(result, ReanimatorPCIe{
			Slot:             pcie.Slot,
			VendorID:         pcie.VendorID,
			DeviceID:         pcie.DeviceID,
			BDF:              pcie.BDF,
			DeviceClass:      normalizeLegacyPCIeDeviceClass(pcie.DeviceClass),
			Manufacturer:     pcie.Manufacturer,
			Model:            model,
			LinkWidth:        pcie.LinkWidth,
			LinkSpeed:        pcie.LinkSpeed,
			MaxLinkWidth:     pcie.MaxLinkWidth,
			MaxLinkSpeed:     pcie.MaxLinkSpeed,
			MACAddresses:     append([]string(nil), pcie.MACAddresses...),
			NUMANode:         pcie.NUMANode,
			SerialNumber:     serialNumber,
			Firmware:         firmware,
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}

	// Convert GPUs as PCIe devices
	for _, gpu := range hw.GPUs {
		serialNumber := strings.TrimSpace(gpu.SerialNumber)

		status := normalizeStatus(gpu.Status, false)
		meta := buildStatusMeta(
			status,
			gpu.StatusCheckedAt,
			gpu.StatusChangedAt,
			gpu.StatusHistory,
			gpu.ErrorDescription,
			collectedAt,
		)

		result = append(result, ReanimatorPCIe{
			Slot:             gpu.Slot,
			VendorID:         gpu.VendorID,
			DeviceID:         gpu.DeviceID,
			BDF:              gpu.BDF,
			DeviceClass:      "VideoController",
			Manufacturer:     gpu.Manufacturer,
			Model:            gpu.Model,
			LinkWidth:        gpu.CurrentLinkWidth,
			LinkSpeed:        gpu.CurrentLinkSpeed,
			MaxLinkWidth:     gpu.MaxLinkWidth,
			MaxLinkSpeed:     gpu.MaxLinkSpeed,
			SerialNumber:     serialNumber,
			Firmware:         gpu.Firmware,
			TemperatureC:     float64(gpu.Temperature),
			PowerW:           float64(gpu.Power),
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}

	// Convert network adapters as PCIe devices
	for _, nic := range hw.NetworkAdapters {
		if !nic.Present {
			continue
		}

		serialNumber := strings.TrimSpace(nic.SerialNumber)

		status := normalizeStatus(nic.Status, false)
		meta := buildStatusMeta(
			status,
			nic.StatusCheckedAt,
			nic.StatusChangedAt,
			nic.StatusHistory,
			nic.ErrorDescription,
			collectedAt,
		)

		result = append(result, ReanimatorPCIe{
			Slot:             nic.Slot,
			VendorID:         nic.VendorID,
			DeviceID:         nic.DeviceID,
			BDF:              "",
			DeviceClass:      normalizeNetworkDeviceClass(nic.PortType, nic.Model, nic.Description),
			Manufacturer:     nic.Vendor,
			Model:            nic.Model,
			LinkWidth:        0,
			LinkSpeed:        "",
			MaxLinkWidth:     0,
			MaxLinkSpeed:     "",
			MACAddresses:     append([]string(nil), nic.MACAddresses...),
			NUMANode:         nic.NUMANode,
			SerialNumber:     serialNumber,
			Firmware:         nic.Firmware,
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}

	return result
}

func isNVSwitchPCIeDevice(pcie models.PCIeDevice) bool {
	deviceClass := strings.TrimSpace(pcie.DeviceClass)
	if strings.EqualFold(deviceClass, "NVSwitch") {
		return true
	}
	slot := normalizeNVSwitchSlotForLookup(pcie.Slot)
	return strings.HasPrefix(slot, "NVSWITCH")
}

func buildNVSwitchFirmwareBySlot(firmware []models.FirmwareInfo) map[string]string {
	result := make(map[string]string)
	for _, fw := range firmware {
		name := strings.TrimSpace(fw.DeviceName)
		if strings.HasPrefix(strings.ToUpper(name), "HGX_FW_EROT_") {
			continue
		}
		slot := ""
		switch {
		case strings.HasPrefix(strings.ToUpper(name), "NVSWITCH "):
			rest := strings.TrimSpace(name[len("NVSwitch "):])
			if rest == "" {
				continue
			}
			slot = rest
			if idx := strings.Index(rest, " ("); idx > 0 {
				slot = strings.TrimSpace(rest[:idx])
			}
		case strings.HasPrefix(strings.ToUpper(name), "HGX_FW_NVSWITCH_"):
			slot = strings.TrimPrefix(strings.ToUpper(name), "HGX_FW_")
		default:
			continue
		}
		slot = normalizeNVSwitchSlotForLookup(slot)
		if slot == "" {
			continue
		}

		if _, exists := result[slot]; exists {
			continue
		}
		version := strings.TrimSpace(fw.Version)
		if version == "" {
			continue
		}
		result[slot] = version
	}
	return result
}

func normalizeNVSwitchSlotForLookup(slot string) string {
	normalized := strings.ToUpper(strings.TrimSpace(slot))
	if strings.HasPrefix(normalized, "NVSWITCHNVSWITCH") {
		return "NVSWITCH" + strings.TrimPrefix(normalized, "NVSWITCHNVSWITCH")
	}
	return normalized
}

func isDisplayClass(deviceClass string) bool {
	class := strings.ToLower(strings.TrimSpace(deviceClass))
	return strings.Contains(class, "display") ||
		strings.Contains(class, "vga") ||
		strings.Contains(class, "3d controller")
}

// convertPowerSupplies converts power supplies to Reanimator format
func convertPowerSupplies(psus []models.PSU, collectedAt string) []ReanimatorPSU {
	if len(psus) == 0 {
		return nil
	}

	result := make([]ReanimatorPSU, 0, len(psus))
	for _, psu := range psus {
		// Skip PSUs without serial number (if not present)
		if !psu.Present || psu.SerialNumber == "" {
			continue
		}

		status := normalizeStatus(psu.Status, false)
		meta := buildStatusMeta(
			status,
			psu.StatusCheckedAt,
			psu.StatusChangedAt,
			psu.StatusHistory,
			psu.ErrorDescription,
			collectedAt,
		)

		result = append(result, ReanimatorPSU{
			Slot:             psu.Slot,
			Model:            psu.Model,
			Vendor:           psu.Vendor,
			WattageW:         psu.WattageW,
			SerialNumber:     psu.SerialNumber,
			PartNumber:       psu.PartNumber,
			Firmware:         psu.Firmware,
			Status:           status,
			InputType:        psu.InputType,
			InputPowerW:      float64(psu.InputPowerW),
			OutputPowerW:     float64(psu.OutputPowerW),
			InputVoltage:     psu.InputVoltage,
			TemperatureC:     float64(psu.TemperatureC),
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}
	return result
}

func seenFirst(seen map[string]struct{}, key string) bool {
	normalized := strings.ToLower(strings.TrimSpace(key))
	if normalized == "" {
		return false
	}
	if _, ok := seen[normalized]; ok {
		return true
	}
	seen[normalized] = struct{}{}
	return false
}

func normalizeSensorStatus(status string) string {
	return normalizeStatus(status, false)
}

type convertedStatusMeta struct {
	StatusCheckedAt  string
	StatusChangedAt  string
	StatusHistory    []ReanimatorStatusHistoryEntry
	ErrorDescription string
}

func buildStatusMeta(
	currentStatus string,
	checkedAt *time.Time,
	changedAt *time.Time,
	history []models.StatusHistoryEntry,
	errorDescription string,
	collectedAt string,
) convertedStatusMeta {
	meta := convertedStatusMeta{
		StatusCheckedAt:  formatOptionalRFC3339(checkedAt),
		StatusChangedAt:  formatOptionalRFC3339(changedAt),
		ErrorDescription: strings.TrimSpace(errorDescription),
	}

	convertedHistory := make([]ReanimatorStatusHistoryEntry, 0, len(history))
	for _, h := range history {
		changed := formatOptionalRFC3339(&h.ChangedAt)
		if changed == "" {
			continue
		}
		convertedHistory = append(convertedHistory, ReanimatorStatusHistoryEntry{
			Status:    normalizeStatus(h.Status, true),
			ChangedAt: changed,
			Details:   strings.TrimSpace(h.Details),
		})
	}
	sort.Slice(convertedHistory, func(i, j int) bool {
		return convertedHistory[i].ChangedAt < convertedHistory[j].ChangedAt
	})
	if len(convertedHistory) > 0 {
		meta.StatusHistory = convertedHistory
		if meta.StatusChangedAt == "" {
			meta.StatusChangedAt = convertedHistory[len(convertedHistory)-1].ChangedAt
		}
	}

	if meta.StatusCheckedAt == "" && len(meta.StatusHistory) > 0 {
		meta.StatusCheckedAt = meta.StatusHistory[len(meta.StatusHistory)-1].ChangedAt
	}
	if meta.StatusCheckedAt == "" && strings.TrimSpace(currentStatus) != "" && collectedAt != "" {
		meta.StatusCheckedAt = collectedAt
	}

	return meta
}

func formatOptionalRFC3339(t *time.Time) string {
	if t == nil || t.IsZero() {
		return ""
	}
	return t.UTC().Format(time.RFC3339)
}

func dedupeFirmware(items []ReanimatorFirmware) []ReanimatorFirmware {
	if len(items) < 2 {
		return items
	}
	seen := make(map[string]struct{}, len(items))
	result := make([]ReanimatorFirmware, 0, len(items))
	for _, item := range items {
		key := strings.ToLower(strings.TrimSpace(item.DeviceName))
		if key == "" {
			key = strings.ToLower(strings.TrimSpace(item.Version))
		}
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		result = append(result, item)
	}
	return result
}

func dedupeCPUs(items []ReanimatorCPU) []ReanimatorCPU {
	if len(items) < 2 {
		return items
	}
	seen := make(map[int]struct{}, len(items))
	result := make([]ReanimatorCPU, 0, len(items))
	for _, item := range items {
		if _, ok := seen[item.Socket]; ok {
			continue
		}
		seen[item.Socket] = struct{}{}
		result = append(result, item)
	}
	return result
}

func dedupeMemory(items []ReanimatorMemory) []ReanimatorMemory {
	if len(items) < 2 {
		return items
	}
	seen := make(map[string]struct{}, len(items))
	result := make([]ReanimatorMemory, 0, len(items))
	for _, item := range items {
		key := strings.ToLower(strings.TrimSpace(item.Slot))
		if key == "" {
			key = strings.ToLower(strings.TrimSpace(item.Location))
		}
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		result = append(result, item)
	}
	return result
}

func dedupeStorage(items []ReanimatorStorage) []ReanimatorStorage {
	if len(items) < 2 {
		return items
	}
	seen := make(map[string]struct{}, len(items))
	result := make([]ReanimatorStorage, 0, len(items))
	for _, item := range items {
		key := strings.ToLower(strings.TrimSpace(item.SerialNumber))
		if key == "" {
			key = "slot:" + strings.ToLower(strings.TrimSpace(item.Slot))
		}
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		result = append(result, item)
	}
	return result
}

func dedupePSUs(items []ReanimatorPSU) []ReanimatorPSU {
	if len(items) < 2 {
		return items
	}
	seen := make(map[string]struct{}, len(items))
	result := make([]ReanimatorPSU, 0, len(items))
	for _, item := range items {
		key := strings.ToLower(strings.TrimSpace(item.SerialNumber))
		if key == "" {
			key = "slot:" + strings.ToLower(strings.TrimSpace(item.Slot))
		}
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		result = append(result, item)
	}
	return result
}

func dedupePCIe(items []ReanimatorPCIe) []ReanimatorPCIe {
	if len(items) < 2 {
		return items
	}
	type scored struct {
		item  ReanimatorPCIe
		score int
		idx   int
	}
	byKey := make(map[string]scored, len(items))
	order := make([]string, 0, len(items))
	for i, item := range items {
		key := pcieDedupKey(item)
		curr := scored{item: item, score: pcieQualityScore(item), idx: i}
		existing, ok := byKey[key]
		if !ok {
			byKey[key] = curr
			order = append(order, key)
			continue
		}
		if curr.score > existing.score {
			byKey[key] = curr
		}
	}
	result := make([]ReanimatorPCIe, 0, len(byKey))
	for _, key := range order {
		result = append(result, byKey[key].item)
	}
	return result
}

func pcieDedupKey(item ReanimatorPCIe) string {
	slot := strings.ToLower(strings.TrimSpace(item.Slot))
	serial := strings.ToLower(strings.TrimSpace(item.SerialNumber))
	bdf := strings.ToLower(strings.TrimSpace(item.BDF))
	// Generic slot names (e.g. "PCIe Device" from HGX BMC) are not unique
	// hardware positions — multiple distinct devices share the same name.
	// Fall through to serial/BDF so they are not incorrectly collapsed.
	if slot != "" && !isGenericPCIeSlotName(slot) {
		return "slot:" + slot
	}
	if serial != "" {
		return "sn:" + serial
	}
	if bdf != "" {
		return "bdf:" + bdf
	}
	if slot != "" {
		return "slot:" + slot
	}
	return strings.ToLower(strings.TrimSpace(item.DeviceClass)) + "|" + strings.ToLower(strings.TrimSpace(item.Model))
}

// isGenericPCIeSlotName reports whether slot is a generic device-type label
// rather than a unique hardware position identifier.
func isGenericPCIeSlotName(slot string) bool {
	switch slot {
	case "pcie device", "pcie slot", "pcie":
		return true
	}
	return false
}

func pcieQualityScore(item ReanimatorPCIe) int {
	score := 0
	if strings.TrimSpace(item.SerialNumber) != "" {
		score += 4
	}
	if strings.TrimSpace(item.Model) != "" && !isGenericPCIeModel(item.Model) {
		score += 3
	}
	status := strings.ToLower(strings.TrimSpace(item.Status))
	if status == "ok" || status == "warning" || status == "critical" {
		score += 2
	}
	if strings.TrimSpace(item.BDF) != "" {
		score++
	}
	if strings.EqualFold(strings.TrimSpace(item.DeviceClass), "DisplayController") {
		score++
	}
	return score
}

func isGenericPCIeModel(model string) bool {
	switch strings.ToLower(strings.TrimSpace(model)) {
	case "", "unknown", "vga", "3d controller", "display controller":
		return true
	default:
		return false
	}
}

// isGenericDeviceClass returns true for Redfish topology class labels that are
// not meaningful device identifiers (e.g. "SingleFunction", "DisplayController").
func isGenericDeviceClass(dc string) bool {
	switch strings.ToLower(strings.TrimSpace(dc)) {
	case "", "pcie device", "display", "display controller", "displaycontroller",
		"vga", "3d controller", "network", "network controller", "network adapter",
		"storage", "storage controller", "other", "unknown",
		"singlefunction", "multifunction", "simulated":
		return true
	default:
		return false
	}
}

// inferCPUManufacturer determines CPU manufacturer from model string
func inferCPUManufacturer(model string) string {
	upper := strings.ToUpper(model)

	// Intel patterns
	if strings.Contains(upper, "INTEL") ||
		strings.Contains(upper, "XEON") ||
		strings.Contains(upper, "CORE I") {
		return "Intel"
	}

	// AMD patterns
	if strings.Contains(upper, "AMD") ||
		strings.Contains(upper, "EPYC") ||
		strings.Contains(upper, "RYZEN") ||
		strings.Contains(upper, "THREADRIPPER") {
		return "AMD"
	}

	// ARM patterns
	if strings.Contains(upper, "ARM") ||
		strings.Contains(upper, "CORTEX") {
		return "ARM"
	}

	// Ampere patterns
	if strings.Contains(upper, "AMPERE") ||
		strings.Contains(upper, "ALTRA") {
		return "Ampere"
	}

	return ""
}

func normalizedSerial(serial string) string {
	s := strings.TrimSpace(serial)
	if s == "" {
		return ""
	}
	switch strings.ToUpper(s) {
	case "N/A", "NA", "NONE", "NULL", "UNKNOWN", "-":
		return ""
	default:
		return s
	}
}

func parseSocketFromSlot(slot string) int {
	s := strings.TrimSpace(strings.ToUpper(slot))
	s = strings.TrimPrefix(s, "CPU")
	if s == "" {
		return 0
	}
	v, err := strconv.Atoi(s)
	if err != nil {
		return 0
	}
	return v
}

func intFromDetailMap(details map[string]any, key string) int {
	if details == nil {
		return 0
	}
	v, ok := details[key]
	if !ok {
		return 0
	}
	switch n := v.(type) {
	case int:
		return n
	case int64:
		return int(n)
	case int32:
		return int(n)
	case float64:
		return int(n)
	case float32:
		return int(n)
	case string:
		i, err := strconv.Atoi(strings.TrimSpace(n))
		if err == nil {
			return i
		}
		return 0
	default:
		return 0
	}
}

func stringFromDetailMap(details map[string]any, key string) string {
	if details == nil {
		return ""
	}
	v, ok := details[key]
	if !ok {
		return ""
	}
	switch s := v.(type) {
	case string:
		return strings.TrimSpace(s)
	default:
		return strings.TrimSpace(fmt.Sprint(s))
	}
}

func int64FromDetailMap(details map[string]any, key string) int64 {
	if details == nil {
		return 0
	}
	v, ok := details[key]
	if !ok {
		return 0
	}
	switch n := v.(type) {
	case int:
		return int64(n)
	case int64:
		return n
	case int32:
		return int64(n)
	case float64:
		return int64(n)
	case float32:
		return int64(n)
	case string:
		i, err := strconv.ParseInt(strings.TrimSpace(n), 10, 64)
		if err == nil {
			return i
		}
		return 0
	default:
		return 0
	}
}

func boolPtrFromDetailMap(details map[string]any, key string) *bool {
	if details == nil {
		return nil
	}
	v, ok := details[key]
	if !ok {
		return nil
	}
	switch b := v.(type) {
	case bool:
		return &b
	case string:
		s := strings.ToLower(strings.TrimSpace(b))
		if s == "true" || s == "1" || s == "yes" {
			value := true
			return &value
		}
		if s == "false" || s == "0" || s == "no" {
			value := false
			return &value
		}
	}
	return nil
}

func boolFromPresentPtr(v *bool, defaultValue bool) bool {
	if v == nil {
		return defaultValue
	}
	return *v
}

func floatFromDetailMap(details map[string]any, key string) float64 {
	if details == nil {
		return 0
	}
	v, ok := details[key]
	if !ok {
		return 0
	}
	switch n := v.(type) {
	case float64:
		return n
	case float32:
		return float64(n)
	case int:
		return float64(n)
	case int64:
		return float64(n)
	case int32:
		return float64(n)
	case string:
		f, err := strconv.ParseFloat(strings.TrimSpace(n), 64)
		if err == nil {
			return f
		}
		return 0
	default:
		return 0
	}
}

func firstNonZeroInt(values ...int) int {
	for _, v := range values {
		if v != 0 {
			return v
		}
	}
	return 0
}

func firstNonZeroFloat(values ...float64) float64 {
	for _, v := range values {
		if v != 0 {
			return v
		}
	}
	return 0
}

func normalizePCIeDeviceClass(d models.HardwareDevice) string {
	switch d.Kind {
	case models.DeviceKindGPU:
		return "VideoController"
	case models.DeviceKindNetwork:
		return normalizeNetworkDeviceClass(d.PortType, d.Model, stringFromDetailMap(d.Details, "description"))
	default:
		return normalizeLegacyPCIeDeviceClass(d.DeviceClass)
	}
}

func normalizeLegacyPCIeDeviceClass(deviceClass string) string {
	switch strings.ToLower(strings.TrimSpace(deviceClass)) {
	case "", "network", "network controller", "networkcontroller", "ethernet", "ethernet controller", "ethernetcontroller":
		return "NetworkController"
	case "fibre channel", "fibre channel controller", "fibrechannelcontroller", "fc":
		return "FibreChannelController"
	case "display", "displaycontroller", "display controller", "vga":
		return "DisplayController"
	case "video", "video controller", "videocontroller", "3d controller":
		return "VideoController"
	case "processing accelerator", "processingaccelerator":
		return "ProcessingAccelerator"
	case "mass storage controller", "massstoragecontroller":
		return "MassStorageController"
	case "storage controller", "storagecontroller":
		return "StorageController"
	default:
		return strings.TrimSpace(deviceClass)
	}
}

func normalizeNetworkDeviceClass(portType, model, description string) string {
	joined := strings.ToLower(strings.TrimSpace(strings.Join([]string{portType, model, description}, " ")))
	switch {
	case strings.Contains(joined, "fibre channel") || strings.Contains(joined, " fibrechannel") || strings.Contains(joined, "fc "):
		return "FibreChannelController"
	default:
		return "NetworkController"
	}
}

func normalizePlaceholderDeviceModel(model string) string {
	trimmed := strings.TrimSpace(model)
	switch strings.ToLower(trimmed) {
	case "", "network device view", "pci device view", "pcie device view", "storage device view":
		return ""
	default:
		return trimmed
	}
}

func hasMeaningfulExporterText(v string) bool {
	s := strings.ToLower(strings.TrimSpace(v))
	if s == "" {
		return false
	}
	switch s {
	case "-", "n/a", "na", "none", "null", "unknown", "network device view", "pci device view", "pcie device view", "storage device view":
		return false
	default:
		return true
	}
}

func isNumericExporterSlot(slot string) bool {
	slot = strings.TrimSpace(slot)
	if slot == "" {
		return false
	}
	for _, r := range slot {
		if r < '0' || r > '9' {
			return false
		}
	}
	return true
}

// inferStorageStatus determines storage device status
func inferStorageStatus(stor models.Storage) string {
	if !stor.Present {
		return "Unknown"
	}
	return "Unknown"
}

func normalizeSourceType(sourceType string) string {
	normalized := strings.ToLower(strings.TrimSpace(sourceType))
	switch normalized {
	case "api", "logfile", "manual":
		return normalized
	case "archive":
		return "logfile"
	default:
		return ""
	}
}

func normalizeProtocol(protocol string) string {
	normalized := strings.ToLower(strings.TrimSpace(protocol))
	switch normalized {
	case "redfish", "ipmi", "snmp", "ssh":
		return normalized
	default:
		return ""
	}
}

func normalizeNullableString(v string) string {
	trimmed := strings.TrimSpace(v)
	if strings.EqualFold(trimmed, "NULL") {
		return ""
	}
	return trimmed
}

func normalizeStatus(status string, allowEmpty bool) string {
	switch strings.ToLower(strings.TrimSpace(status)) {
	case "ok":
		return "OK"
	case "pass":
		return "OK"
	case "warning":
		return "Warning"
	case "critical":
		return "Critical"
	case "fail":
		return "Critical"
	case "unknown":
		return "Unknown"
	case "empty":
		if allowEmpty {
			return "Empty"
		}
		return "Unknown"
	default:
		if allowEmpty {
			return "Unknown"
		}
		return "Unknown"
	}
}

var (
	ipv4Regex = regexp.MustCompile(`(?:^|[^0-9])((?:\d{1,3}\.){3}\d{1,3})(?:[^0-9]|$)`)
)

func inferTargetHost(targetHost, filename string) string {
	if trimmed := strings.TrimSpace(targetHost); trimmed != "" {
		return trimmed
	}

	candidate := strings.TrimSpace(filename)
	if candidate == "" {
		return ""
	}

	if parsed, err := url.Parse(candidate); err == nil && parsed.Hostname() != "" {
		return parsed.Hostname()
	}

	if submatches := ipv4Regex.FindStringSubmatch(candidate); len(submatches) > 1 {
		return submatches[1]
	}

	return ""
}
