package exporter

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

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

var cpuMicrocodeFirmwareRegex = 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(result.CollectedAt)
	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:          convertCPUsFromDevices(devices, collectedAt),
			Memory:        convertMemoryFromDevices(devices, collectedAt),
			Storage:       convertStorageFromDevices(devices, collectedAt),
			PCIeDevices:   convertPCIeFromDevices(devices, collectedAt),
			PowerSupplies: convertPSUsFromDevices(devices, collectedAt),
		},
	}

	return export, nil
}

// 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
	}
	if len(hw.Devices) > 0 {
		return hw.Devices
	}
	hw.Devices = buildDevicesFromLegacy(hw)
	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))
	appendDevice := func(d models.HardwareDevice) {
		all = append(all, d)
	}

	for _, cpu := range hw.CPUs {
		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: map[string]any{
				"socket": cpu.Socket,
			},
		})
	}
	for _, mem := range hw.Memory {
		if !mem.Present || mem.SizeMB == 0 {
			continue
		}
		present := mem.Present
		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: map[string]any{
				"max_speed_mhz":     mem.MaxSpeedMHz,
				"current_speed_mhz": mem.CurrentSpeedMHz,
			},
		})
	}
	for _, stor := range hw.Storage {
		if !stor.Present {
			continue
		}
		present := stor.Present
		appendDevice(models.HardwareDevice{
			Kind:             models.DeviceKindStorage,
			Slot:             stor.Slot,
			Model:            stor.Model,
			Manufacturer:     stor.Manufacturer,
			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,
		})
	}
	for _, pcie := range hw.PCIeDevices {
		appendDevice(models.HardwareDevice{
			Kind:             models.DeviceKindPCIe,
			Slot:             pcie.Slot,
			BDF:              pcie.BDF,
			DeviceClass:      pcie.DeviceClass,
			VendorID:         pcie.VendorID,
			DeviceID:         pcie.DeviceID,
			Model:            pcie.PartNumber,
			PartNumber:       pcie.PartNumber,
			Manufacturer:     pcie.Manufacturer,
			SerialNumber:     pcie.SerialNumber,
			LinkWidth:        pcie.LinkWidth,
			LinkSpeed:        pcie.LinkSpeed,
			MaxLinkWidth:     pcie.MaxLinkWidth,
			MaxLinkSpeed:     pcie.MaxLinkSpeed,
			Status:           pcie.Status,
			StatusCheckedAt:  pcie.StatusCheckedAt,
			StatusChangedAt:  pcie.StatusChangedAt,
			StatusAtCollect:  pcie.StatusAtCollect,
			StatusHistory:    pcie.StatusHistory,
			ErrorDescription: pcie.ErrorDescription,
		})
	}
	for _, gpu := range hw.GPUs {
		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: 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,
			},
		})
	}
	for _, nic := range hw.NetworkAdapters {
		if !nic.Present {
			continue
		}
		present := nic.Present
		appendDevice(models.HardwareDevice{
			Kind:             models.DeviceKindNetwork,
			Slot:             nic.Slot,
			Location:         nic.Location,
			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,
			Present:          &present,
			Status:           nic.Status,
			StatusCheckedAt:  nic.StatusCheckedAt,
			StatusChangedAt:  nic.StatusChangedAt,
			StatusAtCollect:  nic.StatusAtCollect,
			StatusHistory:    nic.StatusHistory,
			ErrorDescription: nic.ErrorDescription,
		})
	}
	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,
		})
	}
	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
	}
	out := make([]models.HardwareDevice, 0, len(order)+len(noKey))
	for _, key := range order {
		out = append(out, byKey[key].item)
	}
	out = append(out, noKey...)
	for i := range out {
		out[i].ID = out[i].Kind + ":" + strconv.Itoa(i)
	}
	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
	}
	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.IsZero() && !secondary.StatusCheckedAt.IsZero() {
		primary.StatusCheckedAt = secondary.StatusCheckedAt
	}
	if primary.StatusChangedAt.IsZero() && !secondary.StatusChangedAt.IsZero() {
		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 {
	if sn := normalizedSerial(item.SerialNumber); sn != "" {
		return "sn:" + strings.ToLower(sn)
	}
	if bdf := strings.ToLower(strings.TrimSpace(item.BDF)); bdf != "" {
		return "bdf:" + bdf
	}
	return ""
}

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) {
			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 string) []ReanimatorCPU {
	result := make([]ReanimatorCPU, 0)
	for _, d := range devices {
		if d.Kind != models.DeviceKindCPU {
			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.StatusAtCollect, 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,
			Manufacturer:     inferCPUManufacturer(d.Model),
			Status:           cpuStatus,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusAtCollect:  meta.StatusAtCollection,
			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 := d.Present != nil && *d.Present
		if !present || d.SizeMB == 0 {
			continue
		}
		status := normalizeStatus(d.Status, true)
		if strings.TrimSpace(d.Status) == "" {
			if present {
				status = "OK"
			} else {
				status = "Empty"
			}
		}
		meta := buildStatusMeta(status, d.StatusCheckedAt, d.StatusChangedAt, d.StatusAtCollect, d.StatusHistory, d.ErrorDescription, collectedAt)
		result = append(result, ReanimatorMemory{
			Slot:             d.Slot,
			Location:         d.Location,
			Present:          present,
			SizeMB:           d.SizeMB,
			Type:             d.Type,
			MaxSpeedMHz:      intFromDetailMap(d.Details, "max_speed_mhz"),
			CurrentSpeedMHz:  intFromDetailMap(d.Details, "current_speed_mhz"),
			Manufacturer:     d.Manufacturer,
			SerialNumber:     d.SerialNumber,
			PartNumber:       d.PartNumber,
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusAtCollect:  meta.StatusAtCollection,
			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 strings.TrimSpace(d.SerialNumber) == "" {
			continue
		}
		present := d.Present == nil || *d.Present
		status := inferStorageStatus(models.Storage{Present: present})
		if strings.TrimSpace(d.Status) != "" {
			status = normalizeStatus(d.Status, false)
		}
		meta := buildStatusMeta(status, d.StatusCheckedAt, d.StatusChangedAt, d.StatusAtCollect, d.StatusHistory, d.ErrorDescription, collectedAt)
		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:          present,
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusAtCollect:  meta.StatusAtCollection,
			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
		}
		deviceClass := d.DeviceClass
		if d.Kind == models.DeviceKindGPU && strings.TrimSpace(deviceClass) == "" {
			deviceClass = "DisplayController"
		}
		model := d.Model
		if model == "" {
			model = d.PartNumber
		}
		temperatureC := d.TemperatureC
		if temperatureC == 0 {
			temperatureC = firstNonZeroInt(
				intFromDetailMap(d.Details, "temperature_c"),
				intFromDetailMap(d.Details, "temperature"),
			)
		}
		powerW := firstNonZeroInt(
			intFromDetailMap(d.Details, "power_w"),
			intFromDetailMap(d.Details, "power"),
		)
		voltageV := firstNonZeroFloat(
			floatFromDetailMap(d.Details, "voltage_v"),
			floatFromDetailMap(d.Details, "voltage"),
			floatFromDetailMap(d.Details, "input_voltage"),
			d.InputVoltage,
		)
		status := normalizeStatus(d.Status, false)
		meta := buildStatusMeta(status, d.StatusCheckedAt, d.StatusChangedAt, d.StatusAtCollect, d.StatusHistory, d.ErrorDescription, collectedAt)
		result = append(result, ReanimatorPCIe{
			Slot:             d.Slot,
			VendorID:         d.VendorID,
			DeviceID:         d.DeviceID,
			BDF:              d.BDF,
			DeviceClass:      deviceClass,
			Manufacturer:     d.Manufacturer,
			Model:            model,
			LinkWidth:        d.LinkWidth,
			LinkSpeed:        d.LinkSpeed,
			MaxLinkWidth:     d.MaxLinkWidth,
			MaxLinkSpeed:     d.MaxLinkSpeed,
			SerialNumber:     normalizedSerial(d.SerialNumber),
			Firmware:         d.Firmware,
			TemperatureC:     temperatureC,
			PowerW:           powerW,
			VoltageV:         voltageV,
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusAtCollect:  meta.StatusAtCollection,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}
	return result
}

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.StatusAtCollect, d.StatusHistory, d.ErrorDescription, collectedAt)
		result = append(result, ReanimatorPSU{
			Slot:             d.Slot,
			Present:          present,
			Model:            d.Model,
			Vendor:           d.Manufacturer,
			WattageW:         d.WattageW,
			SerialNumber:     d.SerialNumber,
			PartNumber:       d.PartNumber,
			Firmware:         d.Firmware,
			Status:           status,
			InputType:        d.InputType,
			InputPowerW:      d.InputPowerW,
			OutputPowerW:     d.OutputPowerW,
			InputVoltage:     d.InputVoltage,
			TemperatureC:     d.TemperatureC,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusAtCollect:  meta.StatusAtCollection,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}
	return result
}

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") {
		return true
	}

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

// 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.StatusAtCollect,
			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,
			Manufacturer:     manufacturer,
			Status:           cpuStatus,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusAtCollect:  meta.StatusAtCollection,
			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 {
		status := normalizeStatus(mem.Status, true)
		if strings.TrimSpace(mem.Status) == "" {
			if mem.Present {
				status = "OK"
			} else {
				status = "Empty"
			}
		}

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

		result = append(result, ReanimatorMemory{
			Slot:             mem.Slot,
			Location:         mem.Location,
			Present:          mem.Present,
			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,
			StatusAtCollect:  meta.StatusAtCollection,
			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 {
		// Skip storage without serial number
		if stor.SerialNumber == "" {
			continue
		}

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

		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:          stor.Present,
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusAtCollect:  meta.StatusAtCollection,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}
	return result
}

// 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
		}

		serialNumber := normalizedSerial(pcie.SerialNumber)

		// Determine model (prefer PartNumber, fallback to DeviceClass)
		model := pcie.PartNumber
		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.StatusAtCollect,
			pcie.StatusHistory,
			pcie.ErrorDescription,
			collectedAt,
		)

		result = append(result, ReanimatorPCIe{
			Slot:             pcie.Slot,
			VendorID:         pcie.VendorID,
			DeviceID:         pcie.DeviceID,
			BDF:              pcie.BDF,
			DeviceClass:      pcie.DeviceClass,
			Manufacturer:     pcie.Manufacturer,
			Model:            model,
			LinkWidth:        pcie.LinkWidth,
			LinkSpeed:        pcie.LinkSpeed,
			MaxLinkWidth:     pcie.MaxLinkWidth,
			MaxLinkSpeed:     pcie.MaxLinkSpeed,
			SerialNumber:     serialNumber,
			Firmware:         firmware,
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusAtCollect:  meta.StatusAtCollection,
			StatusHistory:    meta.StatusHistory,
			ErrorDescription: meta.ErrorDescription,
		})
	}

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

		// Determine device class
		deviceClass := "DisplayController"

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

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

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

		serialNumber := normalizedSerial(nic.SerialNumber)

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

		result = append(result, ReanimatorPCIe{
			Slot:             nic.Slot,
			VendorID:         nic.VendorID,
			DeviceID:         nic.DeviceID,
			BDF:              "",
			DeviceClass:      "NetworkController",
			Manufacturer:     nic.Vendor,
			Model:            nic.Model,
			LinkWidth:        0,
			LinkSpeed:        "",
			MaxLinkWidth:     0,
			MaxLinkSpeed:     "",
			SerialNumber:     serialNumber,
			Firmware:         nic.Firmware,
			Status:           status,
			StatusCheckedAt:  meta.StatusCheckedAt,
			StatusChangedAt:  meta.StatusChangedAt,
			StatusAtCollect:  meta.StatusAtCollection,
			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), "NVSWITCH ") {
			continue
		}

		rest := strings.TrimSpace(name[len("NVSwitch "):])
		if rest == "" {
			continue
		}

		slot := rest
		if idx := strings.Index(rest, " ("); idx > 0 {
			slot = strings.TrimSpace(rest[:idx])
		}
		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.StatusAtCollect,
			psu.StatusHistory,
			psu.ErrorDescription,
			collectedAt,
		)

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

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

func buildStatusMeta(
	currentStatus string,
	checkedAt time.Time,
	changedAt time.Time,
	statusAtCollection *models.StatusAtCollection,
	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 statusAtCollection != nil {
		at := formatOptionalRFC3339(statusAtCollection.At)
		if at != "" && strings.TrimSpace(statusAtCollection.Status) != "" {
			meta.StatusAtCollection = &ReanimatorStatusAtCollection{
				Status: normalizeStatus(statusAtCollection.Status, true),
				At:     at,
			}
		}
	}
	if meta.StatusAtCollection == nil && strings.TrimSpace(currentStatus) != "" && collectedAt != "" {
		meta.StatusAtCollection = &ReanimatorStatusAtCollection{
			Status: currentStatus,
			At:     collectedAt,
		}
	}

	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.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))
	if slot != "" {
		return "slot:" + slot
	}
	if serial != "" {
		return "sn:" + serial
	}
	if bdf != "" {
		return "bdf:" + bdf
	}
	return strings.ToLower(strings.TrimSpace(item.DeviceClass)) + "|" + strings.ToLower(strings.TrimSpace(item.Model))
}

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
	}
}

// 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 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
}

// 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
	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 ""
}
