// Package h3c provides parser for H3C SDS diagnostic archives.
package h3c

import (
	"bufio"
	"encoding/csv"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

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

const (
	parserVersionG5 = "2.1"
	parserVersionG6 = "2.1"
)

func init() {
	parser.Register(&G5Parser{})
	parser.Register(&G6Parser{})
}

// G5Parser implements VendorParser for H3C G5 SDS archives.
type G5Parser struct{}

func (p *G5Parser) Name() string    { return "H3C SDS Parser G5" }
func (p *G5Parser) Vendor() string  { return "h3c_g5" }
func (p *G5Parser) Version() string { return parserVersionG5 }
func (p *G5Parser) Detect(files []parser.ExtractedFile) int {
	return detectH3CG5(files)
}
func (p *G5Parser) Parse(files []parser.ExtractedFile) (*models.AnalysisResult, error) {
	return parseH3CG5(files), nil
}

// G6Parser implements VendorParser for H3C G6 SDS archives.
type G6Parser struct{}

func (p *G6Parser) Name() string    { return "H3C SDS Parser G6" }
func (p *G6Parser) Vendor() string  { return "h3c_g6" }
func (p *G6Parser) Version() string { return parserVersionG6 }
func (p *G6Parser) Detect(files []parser.ExtractedFile) int {
	return detectH3CG6(files)
}
func (p *G6Parser) Parse(files []parser.ExtractedFile) (*models.AnalysisResult, error) {
	return parseH3CG6(files), nil
}

func detectH3CG5(files []parser.ExtractedFile) int {
	confidence := 0
	for _, f := range files {
		path := strings.ToLower(f.Path)
		switch {
		case path == "bmc/pack.info":
			confidence += 6
		case strings.HasSuffix(path, "/fruinfo.ini") || strings.HasSuffix(path, "fruinfo.ini"):
			confidence += 10
		case strings.HasSuffix(path, "/hardware_info.ini") || strings.HasSuffix(path, "hardware_info.ini"):
			confidence += 35
		case strings.HasSuffix(path, "/hardware.info") || strings.HasSuffix(path, "hardware.info"):
			confidence += 24
		case strings.HasSuffix(path, "/firmware_version.ini") || strings.HasSuffix(path, "firmware_version.ini"):
			confidence += 20
		case strings.HasSuffix(path, "/test.csv") || strings.HasSuffix(path, "/test1.csv"):
			confidence += 20
		case strings.HasSuffix(path, "/raid.json") || strings.HasSuffix(path, "raid.json"):
			confidence += 8
		}

		if strings.Contains(path, "fruinfo.ini") && containsH3CMarkers(f.Content) {
			confidence += 10
		}
		if confidence >= 100 {
			return 100
		}
	}
	return minInt(confidence, 100)
}

func detectH3CG6(files []parser.ExtractedFile) int {
	confidence := 0
	for _, f := range files {
		path := strings.ToLower(f.Path)
		switch {
		case path == "bmc/pack.info":
			confidence += 10
		case strings.HasSuffix(path, "/fruinfo.ini") || strings.HasSuffix(path, "fruinfo.ini"):
			confidence += 12
		case strings.HasSuffix(path, "/board_info.ini") || strings.HasSuffix(path, "board_info.ini"):
			confidence += 14
		case strings.HasSuffix(path, "/firmware_version.json") || strings.HasSuffix(path, "firmware_version.json"):
			confidence += 24
		case strings.HasSuffix(path, "/cpudetailinfo.xml") || strings.HasSuffix(path, "cpudetailinfo.xml"):
			confidence += 22
		case strings.HasSuffix(path, "/memorydetailinfo.xml") || strings.HasSuffix(path, "memorydetailinfo.xml"):
			confidence += 22
		case strings.HasSuffix(path, "/sel.json") || strings.HasSuffix(path, "sel.json"):
			confidence += 18
		case strings.HasSuffix(path, "/sensor_info.ini") || strings.HasSuffix(path, "sensor_info.ini"):
			confidence += 8
		}

		if (strings.Contains(path, "board_info.ini") || strings.Contains(path, "fruinfo.ini")) && containsH3CMarkers(f.Content) {
			confidence += 10
		}
		if confidence >= 100 {
			return 100
		}
	}
	return minInt(confidence, 100)
}

func containsH3CMarkers(content []byte) bool {
	s := strings.ToLower(string(content))
	return strings.Contains(s, "h3c") || strings.Contains(s, "new h3c technologies")
}

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

func parseH3CG5(files []parser.ExtractedFile) *models.AnalysisResult {
	result := newAnalysisResult()
	firmwareSeen := make(map[string]struct{})

	if f := parser.FindFileByName(files, "FRUInfo.ini"); f != nil {
		parseFRUInfoINI(f.Content, result)
	}
	if f := parser.FindFileByName(files, "board_info.ini"); f != nil {
		parseBoardInfoINI(f.Content, result, firmwareSeen)
	}
	if f := parser.FindFileByName(files, "board_cfg.ini"); f != nil {
		parseBoardCfgINI(f.Content, result)
	}
	if f := parser.FindFileByName(files, "firmware_version.ini"); f != nil {
		parseFirmwareVersionINI(f.Content, result, firmwareSeen)
	}
	if f := parser.FindFileByName(files, "hardware_info.ini"); f != nil {
		parseHardwareInfoINI(f.Content, result)
	}
	if f := parser.FindFileByName(files, "hardware.info"); f != nil {
		appendUniqueStorages(&result.Hardware.Storage, parseHardwareInfoStorageINI(f.Content))
	}
	if f := parser.FindFileByName(files, "storage_disk.ini"); f != nil {
		appendUniqueStorages(&result.Hardware.Storage, parseStorageINI(f.Content))
	}
	if f := parser.FindFileByName(files, "raid.json"); f != nil {
		result.Hardware.Volumes = append(result.Hardware.Volumes, parseRAIDJSONVolumes(f.Content)...)
	}
	for _, f := range files {
		path := strings.ToLower(f.Path)
		if strings.Contains(path, "storage_raid-") && strings.HasSuffix(path, ".txt") {
			storages, volumes := parseRAIDDetailTXT(f.Content)
			appendUniqueStorages(&result.Hardware.Storage, storages)
			result.Hardware.Volumes = append(result.Hardware.Volumes, volumes...)
		}
	}
	if f := parser.FindFileByName(files, "NVMe_info.txt"); f != nil {
		appendUniqueStorages(&result.Hardware.Storage, parseNVMeInfo(f.Content))
	}
	if f := parser.FindFileByName(files, "Raid_BP_Conf_Info.ini"); f != nil {
		result.Hardware.Devices = append(result.Hardware.Devices, parseRaidBPConfDevices(f.Content)...)
	}
	if f := parser.FindFileByName(files, "psu_cfg.ini"); f != nil {
		appendUniquePSUs(&result.Hardware.PowerSupply, parsePSUCfgINI(f.Content))
	}
	if f := parser.FindFileByName(files, "net_cfg.ini"); f != nil {
		adapters := parseNetCfgNetworkAdapters(f.Content)
		if len(result.Hardware.NetworkAdapters) == 0 {
			appendUniqueNetworkAdapters(&result.Hardware.NetworkAdapters, adapters)
			appendUniqueNICs(&result.Hardware.NetworkCards, networkCardsFromAdapters(adapters))
		}
	}
	enrichStorageWithSmartdata(&result.Hardware.Storage, files)
	if f := parser.FindFileByName(files, "PCIe_arguments_table.xml"); f != nil {
		enrichStorageFromPCIeArguments(&result.Hardware.Storage, f.Content)
	}
	if f := parser.FindFileByName(files, "sensor_info.ini"); f != nil {
		result.Sensors = parseSensorInfoINI(f.Content)
	}

	result.Events = parseSELCSVFiles(files)
	if len(result.Events) == 0 {
		if f := parser.FindFileByName(files, "Sel.json"); f != nil {
			result.Events = parseSELJSON(f.Content)
		}
	}
	if len(result.Events) == 0 {
		if f := parser.FindFileByName(files, "sel_list.txt"); f != nil {
			result.Events = parseSELListTXT(f.Content)
		}
	}
	result.Hardware.Storage = dedupeStorage(result.Hardware.Storage)
	result.Hardware.Volumes = dedupeVolumes(result.Hardware.Volumes)
	parser.ApplyManufacturedYearWeekFromFRU(result.FRU, result.Hardware)

	return result
}

func parseH3CG6(files []parser.ExtractedFile) *models.AnalysisResult {
	result := newAnalysisResult()
	firmwareSeen := make(map[string]struct{})

	if f := parser.FindFileByName(files, "FRUInfo.ini"); f != nil {
		parseFRUInfoINI(f.Content, result)
	}
	if f := parser.FindFileByName(files, "board_info.ini"); f != nil {
		parseBoardInfoINI(f.Content, result, firmwareSeen)
	}
	if f := parser.FindFileByName(files, "firmware_version.json"); f != nil {
		parseFirmwareJSON(f.Content, result, firmwareSeen)
	}
	if f := parser.FindFileByName(files, "CPUDetailInfo.xml"); f != nil {
		parseCPUXML(f.Content, result)
	}
	if f := parser.FindFileByName(files, "MemoryDetailInfo.xml"); f != nil {
		parseMemoryXML(f.Content, result)
	}
	if f := parser.FindFileByName(files, "hardware_info.ini"); f != nil {
		parseHardwareInfoINI(f.Content, result)
	}
	if f := parser.FindFileByName(files, "storage_disk.ini"); f != nil {
		appendUniqueStorages(&result.Hardware.Storage, parseStorageINI(f.Content))
	}
	if f := parser.FindFileByName(files, "raid.json"); f != nil {
		result.Hardware.Volumes = append(result.Hardware.Volumes, parseRAIDJSONVolumes(f.Content)...)
	}
	for _, f := range files {
		path := strings.ToLower(f.Path)
		if strings.Contains(path, "storage_raid-") && strings.HasSuffix(path, ".txt") {
			storages, volumes := parseRAIDDetailTXT(f.Content)
			appendUniqueStorages(&result.Hardware.Storage, storages)
			result.Hardware.Volumes = append(result.Hardware.Volumes, volumes...)
		}
	}
	if f := parser.FindFileByName(files, "NVMe_info.txt"); f != nil {
		appendUniqueStorages(&result.Hardware.Storage, parseNVMeInfo(f.Content))
	}
	if f := parser.FindFileByName(files, "psu_cfg.ini"); f != nil {
		appendUniquePSUs(&result.Hardware.PowerSupply, parsePSUCfgINI(f.Content))
	}
	if f := parser.FindFileByName(files, "net_cfg.ini"); f != nil {
		adapters := parseNetCfgNetworkAdapters(f.Content)
		if len(result.Hardware.NetworkAdapters) == 0 {
			appendUniqueNetworkAdapters(&result.Hardware.NetworkAdapters, adapters)
			appendUniqueNICs(&result.Hardware.NetworkCards, networkCardsFromAdapters(adapters))
		}
	}
	if f := parser.FindFileByName(files, "PCIe_arguments_table.xml"); f != nil {
		enrichStorageFromPCIeArguments(&result.Hardware.Storage, f.Content)
	}
	if f := parser.FindFileByName(files, "sensor_info.ini"); f != nil {
		result.Sensors = parseSensorInfoINI(f.Content)
	}

	if f := parser.FindFileByName(files, "Sel.json"); f != nil {
		result.Events = parseSELJSON(f.Content)
	}
	if len(result.Events) == 0 {
		if f := parser.FindFileByName(files, "sel_list.txt"); f != nil {
			result.Events = parseSELListTXT(f.Content)
		}
	}
	result.Hardware.Storage = dedupeStorage(result.Hardware.Storage)
	result.Hardware.Volumes = dedupeVolumes(result.Hardware.Volumes)
	parser.ApplyManufacturedYearWeekFromFRU(result.FRU, result.Hardware)

	return result
}

func parseFRUInfoINI(content []byte, result *models.AnalysisResult) {
	sections := parseLooseINI(string(content))
	names := sortedSectionNames(sections)

	for _, sectionName := range names {
		section := sections[sectionName]
		fru := models.FRUInfo{
			DeviceID:     sectionName,
			Description:  sectionName,
			ChassisType:  getSectionValue(section, "Chassis Type"),
			Manufacturer: getSectionValue(section, "Board Manufacturer", "Product Manufacturer"),
			ProductName:  getSectionValue(section, "Product Product Name", "Board Product Name"),
			SerialNumber: getSectionValue(section, "Product Serial Number", "Board Top Serial Number", "Board Serial Number", "Chassis Serial Number"),
			PartNumber:   getSectionValue(section, "Product Part Number", "Board Part Number", "Chassis Part Number"),
			Version:      getSectionValue(section, "Product Version"),
			MfgDate:      getSectionValue(section, "Mfg.Date"),
			AssetTag:     getSectionValue(section, "Product Asset Tag"),
		}

		if isEmptyFRU(fru) {
			continue
		}
		result.FRU = append(result.FRU, fru)

		if strings.EqualFold(sectionName, "baseboard") {
			applyBoardFromFRU(&result.Hardware.BoardInfo, fru)
		}
	}
}

func parseBoardInfoINI(content []byte, result *models.AnalysisResult, firmwareSeen map[string]struct{}) {
	sections := parseLooseINI(string(content))
	names := sortedSectionNames(sections)

	for _, sectionName := range names {
		section := sections[sectionName]

		if strings.EqualFold(sectionName, "System board") {
			board := &result.Hardware.BoardInfo
			setIfEmpty(&board.Manufacturer, getSectionValue(section, "BoardMfr"))
			setIfEmpty(&board.ProductName, getSectionValue(section, "BoardProductName"))
			setIfEmpty(&board.SerialNumber, getSectionValue(section, "BoardSerialNum"))
			setIfEmpty(&board.PartNumber, getSectionValue(section, "BoardPartNum"))
			setIfEmpty(&board.Version, getSectionValue(section, "PCB Version"))
		}

		for key, value := range section {
			keyTrim := strings.TrimSpace(key)
			valueTrim := strings.TrimSpace(value)
			if valueTrim == "" || strings.EqualFold(valueTrim, "N/A") {
				continue
			}
			if !strings.Contains(strings.ToLower(keyTrim), "version") {
				continue
			}
			deviceName := strings.TrimSpace(fmt.Sprintf("%s %s", sectionName, keyTrim))
			appendFirmwareUnique(result, firmwareSeen, models.FirmwareInfo{
				DeviceName: deviceName,
				Version:    valueTrim,
			})
		}
	}
}

func parseFirmwareVersionINI(content []byte, result *models.AnalysisResult, firmwareSeen map[string]struct{}) {
	sections := parseLooseINI(string(content))
	names := sortedSectionNames(sections)

	for _, sectionName := range names {
		section := sections[sectionName]

		if strings.EqualFold(sectionName, "System board") {
			setIfEmpty(&result.Hardware.BoardInfo.Version, getSectionValue(section, "PCB Version"))
		}

		for key, value := range section {
			keyTrim := strings.TrimSpace(key)
			valueTrim := strings.TrimSpace(value)
			if valueTrim == "" || strings.EqualFold(valueTrim, "N/A") {
				continue
			}

			normKey := normalizeKey(keyTrim)
			if !strings.Contains(normKey, "version") {
				continue
			}

			appendFirmwareUnique(result, firmwareSeen, models.FirmwareInfo{
				DeviceName: strings.TrimSpace(fmt.Sprintf("%s %s", sectionName, keyTrim)),
				Version:    valueTrim,
			})
		}
	}
}

func parseHardwareInfoINI(content []byte, result *models.AnalysisResult) {
	sections := parseLooseINI(string(content))
	names := sortedSectionNames(sections)

	for _, sectionName := range names {
		section := sections[sectionName]
		normSection := normalizeKey(sectionName)

		switch {
		case strings.HasPrefix(normSection, "processorsprocessor"):
			cpu := parseCPUFromHardwareInfoSection(sectionName, section)
			if cpu.Model == "" {
				continue
			}
			if idx := findCPUIndex(result.Hardware.CPUs, cpu); idx >= 0 {
				mergeCPU(&result.Hardware.CPUs[idx], cpu)
				continue
			}
			result.Hardware.CPUs = append(result.Hardware.CPUs, cpu)
		case strings.HasPrefix(normSection, "memorydetailsdimmindex"):
			dimm := parseDIMMFromHardwareInfoSection(sectionName, section)
			if dimm.Slot == "" && dimm.SerialNumber == "" {
				continue
			}
			if idx := findMemoryIndex(result.Hardware.Memory, dimm); idx >= 0 {
				mergeMemoryDIMM(&result.Hardware.Memory[idx], dimm)
				continue
			}
			result.Hardware.Memory = append(result.Hardware.Memory, dimm)
		}
	}

	adapters := parseHardwareInfoNetworkAdapters(content)
	appendUniqueNetworkAdapters(&result.Hardware.NetworkAdapters, adapters)
	appendUniqueNICs(&result.Hardware.NetworkCards, networkCardsFromAdapters(result.Hardware.NetworkAdapters))
}

func parseHardwareInfoNetworkAdapters(content []byte) []models.NetworkAdapter {
	blocks := parseKeyValueBlocks(string(content))
	out := make([]models.NetworkAdapter, 0)

	for _, block := range blocks {
		deviceType := strings.ToLower(strings.TrimSpace(getSectionValue(block, "Device Type")))
		hasNetworkHints := deviceType == "nic" ||
			getSectionValue(block, "Network Port") != "" ||
			getSectionValue(block, "MAC Address") != "" ||
			getSectionValue(block, "Vendor ID") != "" ||
			getSectionValue(block, "Device ID") != ""
		if !hasNetworkHints {
			continue
		}

		slot := normalizePCIeSlotLabel(getSectionValue(block, "Location", "Slot", "PCIe Slot"))
		if slot == "" {
			slot = firstNonEmpty(getSectionValue(block, "Network Port"), "NIC")
		}

		mac := normalizeMACAddress(getSectionValue(block, "MAC Address"))
		productName := normalizeUnknownString(getSectionValue(block, "Product Name", "Model"))
		vendorID := parseMaybeInt(getSectionValue(block, "Vendor ID"))
		deviceID := parseMaybeInt(getSectionValue(block, "Device ID"))
		vendor := normalizeUnknownString(getSectionValue(block, "Vendor", "Manufacturer"))
		if vendor == "" && vendorID > 0 {
			vendor = strings.TrimSpace(pciids.VendorName(vendorID))
		}

		networkPort := normalizeUnknownString(getSectionValue(block, "Network Port"))
		speed := normalizeUnknownString(firstNonEmpty(getSectionValue(block, "Current Speed"), getSectionValue(block, "Speed")))
		bandwidth := normalizeUnknownString(getSectionValue(block, "Current Bandwidth", "Link Width"))
		status := normalizeComponentStatus(firstNonEmpty(getSectionValue(block, "Network Status"), getSectionValue(block, "Status")))
		if status == "" {
			status = "ok"
		}

		descParts := make([]string, 0, 3)
		if networkPort != "" {
			descParts = append(descParts, networkPort)
		}
		if speed != "" {
			descParts = append(descParts, "speed "+speed)
		}
		if bandwidth != "" {
			descParts = append(descParts, "link "+bandwidth)
		}

		adapter := models.NetworkAdapter{
			Slot:         slot,
			Location:     slot,
			Present:      status != "absent",
			Model:        firstNonEmpty(productName, "Ethernet Adapter"),
			Description:  strings.Join(descParts, "; "),
			Vendor:       vendor,
			VendorID:     vendorID,
			DeviceID:     deviceID,
			SerialNumber: normalizeUnknownString(getSectionValue(block, "Serial Number", "SerialNumber", "SN")),
			PartNumber:   normalizeUnknownString(getSectionValue(block, "Part Number", "PartNumber", "PN")),
			Firmware:     normalizeUnknownString(getSectionValue(block, "Firmware Version", "Firmware")),
			PortCount:    inferPortCountFromNetworkFields(productName, networkPort),
			Status:       status,
		}
		if mac != "" {
			adapter.MACAddresses = []string{mac}
			if adapter.PortCount == 0 {
				adapter.PortCount = 1
			}
		}

		if isNetworkAdapterEmpty(adapter) {
			continue
		}
		out = append(out, adapter)
	}

	return out
}

func parseCPUFromHardwareInfoSection(sectionName string, section map[string]string) models.CPU {
	socket := parseSectionIndex(sectionName, `(?i)processor\s+(\d+)`)
	statusRaw := getSectionValue(section, "Status")

	return models.CPU{
		Socket:       socket,
		Model:        getSectionValue(section, "Model"),
		Cores:        parseMaybeIntLoose(getSectionValue(section, "Cores")),
		Threads:      parseMaybeIntLoose(getSectionValue(section, "Threads")),
		FrequencyMHz: parseMaybeIntLoose(getSectionValue(section, "Frequency", "Processor Speed")),
		L1CacheKB:    parseMaybeIntLoose(getSectionValue(section, "L1 Cache")),
		L2CacheKB:    parseMaybeIntLoose(getSectionValue(section, "L2 Cache")),
		L3CacheKB:    parseMaybeIntLoose(getSectionValue(section, "L3 Cache")),
		PPIN:         getSectionValue(section, "CPU PPIN", "PPIN"),
		SerialNumber: getSectionValue(section, "Processor ID", "Serial Number"),
		Status:       normalizeComponentStatus(statusRaw),
	}
}

func parseDIMMFromHardwareInfoSection(sectionName string, section map[string]string) models.MemoryDIMM {
	statusRaw := getSectionValue(section, "Status")
	status := normalizeComponentStatus(statusRaw)
	present := status != "absent"

	slot := firstNonEmpty(
		getSectionValue(section, "Socket ID", "Slot", "Name"),
		sectionName,
	)
	location := strings.TrimSpace(getSectionValue(section, "Location"))
	channel := strings.TrimSpace(getSectionValue(section, "Channel"))
	if location != "" && channel != "" {
		location = location + " CH" + channel
	}

	return models.MemoryDIMM{
		Slot:            slot,
		Location:        location,
		Present:         present,
		SizeMB:          parseMaybeIntLoose(getSectionValue(section, "Size", "DIMM Size")),
		Type:            firstNonEmpty(getSectionValue(section, "Type"), getSectionValue(section, "Technology"), "DIMM"),
		Technology:      getSectionValue(section, "Technology"),
		MaxSpeedMHz:     parseMaybeIntLoose(getSectionValue(section, "Maximum Frequency", "Max Frequency")),
		CurrentSpeedMHz: parseMaybeIntLoose(getSectionValue(section, "Current Frequency", "Operating Frequency")),
		Manufacturer:    getSectionValue(section, "Manufacture", "Manufacturer"),
		SerialNumber:    getSectionValue(section, "Serial Number", "SerialNumber"),
		PartNumber:      getSectionValue(section, "Part Number"),
		Status:          status,
		Ranks:           parseMaybeIntLoose(getSectionValue(section, "Ranks")),
	}
}

func parseHardwareInfoStorageINI(content []byte) []models.Storage {
	sections := parseLooseINI(string(content))
	names := sortedSectionNames(sections)
	out := make([]models.Storage, 0)

	for _, sectionName := range names {
		section := sections[sectionName]
		normSection := normalizeKey(sectionName)
		if !strings.HasPrefix(normSection, "disk") && !strings.HasPrefix(normSection, "nvme") {
			continue
		}

		slotNum := getSectionValue(section, "SlotNum")
		frontRear := getSectionValue(section, "FrontOrRear")
		slot := firstNonEmpty(
			getSectionValue(section, "NvmePhySlot", "DiskSlotDesc", "Position", "Slot"),
			formatSlot(frontRear, slotNum),
			slotNum,
			sectionName,
		)

		presentRaw := getSectionValue(section, "Present")
		present := true
		if strings.TrimSpace(presentRaw) != "" {
			present = isTruthy(presentRaw)
		}
		status := normalizeStorageStatus(getSectionValue(section, "Status"), present)
		if status == "" {
			status = normalizeStorageStatus(presentRaw, present)
		}

		storageType := "disk"
		if strings.HasPrefix(normSection, "nvme") || strings.Contains(strings.ToLower(slot), "nvme") {
			storageType = "nvme"
		}

		location := getSectionValue(section, "FrontOrRear", "Location")
		if location == "" {
			slotLower := strings.ToLower(slot)
			switch {
			case strings.Contains(slotLower, "front"):
				location = "Front"
			case strings.Contains(slotLower, "rear"):
				location = "Rear"
			}
		}

		item := models.Storage{
			Slot:         slot,
			Type:         storageType,
			SerialNumber: getSectionValue(section, "SerialNumber"),
			Present:      present,
			Location:     location,
			Status:       status,
		}
		if isStorageEmpty(item) {
			continue
		}
		out = append(out, item)
	}

	return dedupeStorage(out)
}

func parseSmartdataStorages(files []parser.ExtractedFile) []models.Storage {
	out := make([]models.Storage, 0)

	for _, f := range files {
		path := strings.ToLower(strings.TrimSpace(f.Path))
		if !strings.HasPrefix(path, "static/smartdata/") || !strings.HasSuffix(path, ".txt") {
			continue
		}

		model, serial := parseSmartdataDevice(f.Content)
		if serial == "" {
			continue
		}

		slot := smartdataSlotFromPath(f.Path)
		st := models.Storage{
			Slot:         slot,
			Type:         inferStorageTypeFromValues(slot, "", "", model),
			Model:        model,
			SerialNumber: serial,
			Present:      true,
			Status:       "ok",
		}
		if st.Type == "" {
			st.Type = "disk"
		}
		out = append(out, st)
	}

	return dedupeStorage(out)
}

func enrichStorageWithSmartdata(storages *[]models.Storage, files []parser.ExtractedFile) {
	for _, add := range parseSmartdataStorages(files) {
		idx := findStorageIndex(*storages, add)
		if idx < 0 {
			continue
		}
		mergeStorage(&(*storages)[idx], add)
	}
}

type pcieStorageProfile struct {
	Model        string
	Manufacturer string
	IsNVMe       bool
}

func enrichStorageFromPCIeArguments(storages *[]models.Storage, content []byte) {
	profiles := parseStorageProfilesFromPCIeArgumentsXML(content)
	if len(profiles) == 0 {
		return
	}

	nvmeVendor, nvmeModel := chooseNVMeProfileFallback(*storages, profiles)
	nonNVMeVendor, nonNVMeModel := selectUniqueVendorModel(filterPCIeStorageProfilesByNVMe(profiles, false))

	for i := range *storages {
		s := &(*storages)[i]
		isNVMeStorage := strings.EqualFold(strings.TrimSpace(s.Type), "nvme")

		vendor, model := nonNVMeVendor, nonNVMeModel
		if isNVMeStorage {
			vendor, model = nvmeVendor, nvmeModel
		}
		if vendor == "" && model == "" {
			continue
		}
		if strings.TrimSpace(s.Manufacturer) == "" && vendor != "" {
			s.Manufacturer = vendor
		}
		if strings.TrimSpace(s.Model) == "" {
			if model != "" {
				s.Model = model
			} else if vendor != "" && isNVMeStorage {
				s.Model = "NVMe SSD"
			}
		}
	}
}

func chooseNVMeProfileFallback(storages []models.Storage, profiles []pcieStorageProfile) (vendor, model string) {
	nvmeProfiles := filterPCIeStorageProfilesByNVMe(profiles, true)
	if len(nvmeProfiles) == 0 {
		return "", ""
	}

	vendor, model = selectUniqueVendorModel(nvmeProfiles)
	if vendor != "" || model != "" {
		return vendor, model
	}

	frontBayNVMe := false
	for _, s := range storages {
		if strings.ToLower(strings.TrimSpace(s.Type)) != "nvme" {
			continue
		}
		slotLower := strings.ToLower(strings.TrimSpace(s.Slot))
		if strings.Contains(slotLower, "front") {
			frontBayNVMe = true
			break
		}
		if n := parseMaybeInt(slotLower); n >= 100 {
			frontBayNVMe = true
			break
		}
	}
	if !frontBayNVMe {
		return "", ""
	}

	sffProfiles := make([]pcieStorageProfile, 0)
	for _, p := range nvmeProfiles {
		nameLower := strings.ToLower(strings.TrimSpace(p.Model))
		if strings.Contains(nameLower, "sff") {
			sffProfiles = append(sffProfiles, p)
		}
	}
	if len(sffProfiles) == 0 {
		return "", ""
	}
	return selectUniqueVendorModel(sffProfiles)
}

func selectUniqueVendorModel(profiles []pcieStorageProfile) (vendor, model string) {
	vendorSet := make(map[string]struct{})
	modelSet := make(map[string]struct{})
	for _, p := range profiles {
		if strings.TrimSpace(p.Manufacturer) != "" {
			vendorSet[p.Manufacturer] = struct{}{}
		}
		if strings.TrimSpace(p.Model) != "" && !strings.EqualFold(strings.TrimSpace(p.Model), "N/A") {
			modelSet[p.Model] = struct{}{}
		}
	}

	if len(vendorSet) == 1 {
		for v := range vendorSet {
			vendor = v
		}
	}
	if len(modelSet) == 1 {
		for m := range modelSet {
			model = m
		}
	}
	return vendor, model
}

func filterPCIeStorageProfilesByNVMe(profiles []pcieStorageProfile, isNVMe bool) []pcieStorageProfile {
	out := make([]pcieStorageProfile, 0, len(profiles))
	for _, p := range profiles {
		if p.IsNVMe == isNVMe {
			out = append(out, p)
		}
	}
	return out
}

func parseStorageProfilesFromPCIeArgumentsXML(content []byte) []pcieStorageProfile {
	root, ok := parseXMLRoot(content)
	if !ok {
		return nil
	}

	out := make([]pcieStorageProfile, 0)
	seen := make(map[string]struct{})

	for _, node := range root.Nodes {
		name := strings.ToUpper(strings.TrimSpace(node.XMLName.Local))
		if !strings.HasPrefix(name, "PCIE") {
			continue
		}

		base := findChildXMLNode(node, "base_args")
		typeGet := findChildXMLNode(node, "type_get_args")
		if base == nil || typeGet == nil {
			continue
		}

		baseFields := xmlNodeFields(*base)
		itemName := strings.TrimSpace(baseFields["name"])
		itemType := strings.ToLower(strings.TrimSpace(baseFields["type"]))
		if itemName == "" {
			continue
		}

		lowerName := strings.ToLower(itemName)
		if strings.HasPrefix(strings.ToUpper(itemName), "EX-") {
			continue
		}
		isNVMe := strings.Contains(lowerName, "nvme") || strings.Contains(itemType, "nvme")
		if !isNVMe &&
			!containsAny(lowerName, "ssd", "hdd", "disk", "sas", "sata") &&
			!containsAny(itemType, "ssd", "hdd", "disk", "sas", "sata") {
			continue
		}

		bios := findChildXMLNode(*typeGet, "bios_args")
		if bios == nil {
			continue
		}
		vendorRaw := strings.TrimSpace(xmlNodeFields(*bios)["vendor_id"])
		vendorID := parseMaybeInt(vendorRaw)
		if vendorID == 0 || strings.EqualFold(vendorRaw, "0xFFFF") {
			continue
		}

		vendorName := strings.TrimSpace(pciids.VendorName(vendorID))
		if vendorName == "" {
			vendorName = fmt.Sprintf("0x%X", vendorID)
		}

		profile := pcieStorageProfile{
			Model:        itemName,
			Manufacturer: vendorName,
			IsNVMe:       isNVMe,
		}
		key := strings.ToLower(profile.Model + "|" + profile.Manufacturer + "|" + strconv.FormatBool(profile.IsNVMe))
		if _, exists := seen[key]; exists {
			continue
		}
		seen[key] = struct{}{}
		out = append(out, profile)
	}

	return out
}

func findChildXMLNode(parent xmlNode, childName string) *xmlNode {
	target := strings.ToLower(strings.TrimSpace(childName))
	for i := range parent.Nodes {
		if strings.ToLower(strings.TrimSpace(parent.Nodes[i].XMLName.Local)) == target {
			return &parent.Nodes[i]
		}
	}
	return nil
}

var (
	netCfgHeaderRE   = regexp.MustCompile(`^([^\s]+)\s+Link\s+encap:`)
	netCfgHWAddrRE   = regexp.MustCompile(`(?i)\bHWaddr\s+([0-9a-f]{2}(?::[0-9a-f]{2}){5})`)
	netCfgInet4OldRE = regexp.MustCompile(`\binet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)`)
	netCfgInet4NewRE = regexp.MustCompile(`\binet\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(?:/[0-9]+)?`)
	netCfgInet6OldRE = regexp.MustCompile(`\binet6 addr:\s*([0-9a-f:]+(?:/[0-9]+)?)`)
	netCfgInet6NewRE = regexp.MustCompile(`\binet6\s+([0-9a-f:]+(?:/[0-9]+)?)`)
	pcieSlotDigitsRE = regexp.MustCompile(`(?i)(\d+)`)
	portCountPattRE  = regexp.MustCompile(`(?i)(\d+)\s*p\b`)
	portCountMulRE   = regexp.MustCompile(`(?i)(\d+)\s*\*\s*\d+\s*g`)
	portSuffixRE     = regexp.MustCompile(`(?i)port\s+(\d+)`)
)

type netCfgAdapterState struct {
	Slot  string
	Names []string
	MACs  []string
	IPv4  []string
	IPv6  []string
	Up    bool
}

func parseNetCfgNetworkAdapters(content []byte) []models.NetworkAdapter {
	lines := strings.Split(strings.ReplaceAll(string(content), "\r\n", "\n"), "\n")
	byKey := make(map[string]*netCfgAdapterState)
	order := make([]string, 0)

	var current *netCfgAdapterState
	var currentName string

	for _, raw := range lines {
		line := strings.TrimRight(raw, "\r")
		trimmed := strings.TrimSpace(line)
		if trimmed == "" {
			current = nil
			currentName = ""
			continue
		}
		if strings.HasPrefix(trimmed, "[") && strings.HasSuffix(trimmed, "]") {
			current = nil
			currentName = ""
			continue
		}

		if !strings.HasPrefix(line, " ") && !strings.HasPrefix(line, "\t") {
			m := netCfgHeaderRE.FindStringSubmatch(line)
			if len(m) != 2 {
				current = nil
				currentName = ""
				continue
			}

			ifaceName := strings.TrimSpace(m[1])
			baseName := baseInterfaceName(ifaceName)
			if strings.EqualFold(baseName, "lo") {
				current = nil
				currentName = ""
				continue
			}

			mac := normalizeMACAddress(extractFirstRegexpGroup(line, netCfgHWAddrRE))
			key := "if:" + strings.ToLower(baseName)
			if mac != "" {
				key = "mac:" + strings.ToLower(mac)
			}

			state, ok := byKey[key]
			if !ok {
				state = &netCfgAdapterState{Slot: baseName}
				byKey[key] = state
				order = append(order, key)
			}
			if state.Slot == "" {
				state.Slot = baseName
			}
			appendStringUnique(&state.Names, ifaceName)
			if mac != "" {
				appendStringUnique(&state.MACs, mac)
			}

			current = state
			currentName = ifaceName
			continue
		}

		if current == nil {
			continue
		}

		upper := strings.ToUpper(trimmed)
		if strings.Contains(upper, "UP") && !strings.Contains(upper, "DOWN") {
			current.Up = true
		}
		appendMatchesUnique(&current.IPv4, trimmed, netCfgInet4OldRE)
		appendMatchesUnique(&current.IPv4, trimmed, netCfgInet4NewRE)
		appendMatchesUnique(&current.IPv6, strings.ToLower(trimmed), netCfgInet6OldRE)
		appendMatchesUnique(&current.IPv6, strings.ToLower(trimmed), netCfgInet6NewRE)
		if currentName != "" {
			appendStringUnique(&current.Names, currentName)
		}
	}

	out := make([]models.NetworkAdapter, 0, len(order))
	for _, key := range order {
		state := byKey[key]
		if state == nil {
			continue
		}
		if strings.TrimSpace(state.Slot) == "" && len(state.MACs) == 0 {
			continue
		}

		status := "down"
		if state.Up {
			status = "ok"
		}

		descParts := make([]string, 0, 3)
		if len(state.Names) > 0 {
			descParts = append(descParts, "interfaces: "+strings.Join(state.Names, ", "))
		}
		if len(state.IPv4) > 0 {
			descParts = append(descParts, "ipv4: "+strings.Join(state.IPv4, ", "))
		}
		if len(state.IPv6) > 0 {
			descParts = append(descParts, "ipv6: "+strings.Join(state.IPv6, ", "))
		}

		out = append(out, models.NetworkAdapter{
			Slot:         strings.TrimSpace(state.Slot),
			Location:     strings.TrimSpace(firstNonEmpty(state.Slot, firstSliceValue(state.Names))),
			Present:      true,
			Model:        "Ethernet Interface",
			Description:  strings.Join(descParts, "; "),
			PortCount:    maxInt(1, len(state.MACs)),
			MACAddresses: append([]string(nil), state.MACs...),
			Status:       status,
		})
	}

	return out
}

func networkCardsFromAdapters(items []models.NetworkAdapter) []models.NIC {
	out := make([]models.NIC, 0, len(items))
	for _, item := range items {
		mac := firstSliceValue(item.MACAddresses)
		if strings.TrimSpace(item.Model) == "" && strings.TrimSpace(mac) == "" {
			continue
		}
		out = append(out, models.NIC{
			Name:        firstNonEmpty(item.Slot, item.Location, "NIC"),
			Model:       firstNonEmpty(item.Model, "Ethernet Interface"),
			Description: item.Description,
			MACAddress:  mac,
		})
	}
	return out
}

func extractFirstRegexpGroup(s string, re *regexp.Regexp) string {
	if re == nil {
		return ""
	}
	m := re.FindStringSubmatch(s)
	if len(m) < 2 {
		return ""
	}
	return strings.TrimSpace(m[1])
}

func appendMatchesUnique(dst *[]string, text string, re *regexp.Regexp) {
	if re == nil {
		return
	}
	matches := re.FindAllStringSubmatch(text, -1)
	for _, m := range matches {
		if len(m) < 2 {
			continue
		}
		appendStringUnique(dst, strings.TrimSpace(m[1]))
	}
}

func appendStringUnique(dst *[]string, v string) {
	v = strings.TrimSpace(v)
	if v == "" {
		return
	}
	for _, cur := range *dst {
		if strings.EqualFold(strings.TrimSpace(cur), v) {
			return
		}
	}
	*dst = append(*dst, v)
}

func normalizeMACAddress(s string) string {
	s = strings.ToUpper(strings.TrimSpace(s))
	if len(s) == 17 {
		return s
	}
	s = strings.ReplaceAll(s, "-", "")
	s = strings.ReplaceAll(s, ":", "")
	if len(s) != 12 {
		return ""
	}
	parts := make([]string, 0, 6)
	for i := 0; i < len(s); i += 2 {
		parts = append(parts, s[i:i+2])
	}
	return strings.Join(parts, ":")
}

func baseInterfaceName(name string) string {
	name = strings.TrimSpace(name)
	if idx := strings.Index(name, "."); idx > 0 {
		return strings.TrimSpace(name[:idx])
	}
	return name
}

func firstSliceValue(items []string) string {
	for _, item := range items {
		if strings.TrimSpace(item) != "" {
			return strings.TrimSpace(item)
		}
	}
	return ""
}

func normalizeUnknownString(s string) string {
	s = strings.TrimSpace(s)
	if s == "" {
		return ""
	}
	lower := strings.ToLower(s)
	if lower == "n/a" || lower == "na" || lower == "-" || lower == "unknown" || lower == "none" {
		return ""
	}
	return s
}

func normalizePCIeSlotLabel(raw string) string {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return ""
	}
	if strings.EqualFold(raw, "N/A") || strings.EqualFold(raw, "-") {
		return ""
	}
	m := pcieSlotDigitsRE.FindStringSubmatch(raw)
	if len(m) >= 2 {
		if n := parseMaybeInt(m[1]); n > 0 {
			return fmt.Sprintf("PCIe %d", n)
		}
	}
	return raw
}

func inferPortCountFromNetworkFields(model, networkPort string) int {
	modelLower := strings.ToLower(strings.TrimSpace(model))
	if modelLower != "" {
		if m := portCountPattRE.FindStringSubmatch(modelLower); len(m) == 2 {
			if n := parseMaybeInt(m[1]); n > 0 {
				return n
			}
		}
		if m := portCountMulRE.FindStringSubmatch(modelLower); len(m) == 2 {
			if n := parseMaybeInt(m[1]); n > 0 {
				return n
			}
		}
	}
	if m := portSuffixRE.FindStringSubmatch(strings.ToLower(strings.TrimSpace(networkPort))); len(m) == 2 {
		if n := parseMaybeInt(m[1]); n > 0 {
			return n
		}
	}
	return 0
}

func parseSmartdataDevice(content []byte) (model, serial string) {
	scanner := bufio.NewScanner(strings.NewReader(string(content)))
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || !strings.Contains(line, ":") {
			continue
		}

		key, value, ok := parseColonKVLine(line)
		if !ok {
			continue
		}
		keyNorm := normalizeKey(key)
		val := strings.TrimSpace(value)
		if val == "" {
			continue
		}

		switch keyNorm {
		case "modelinfo", "devicemodel", "modelnumber":
			if model == "" {
				model = val
			}
		case "serialnumber":
			if serial == "" {
				serial = val
			}
		}
	}

	return strings.TrimSpace(model), strings.TrimSpace(serial)
}

func smartdataSlotFromPath(path string) string {
	path = strings.ReplaceAll(path, "\\", "/")
	parts := strings.Split(path, "/")
	for i := range parts {
		if strings.EqualFold(parts[i], "smartdata") && i+1 < len(parts) {
			return strings.TrimSpace(parts[i+1])
		}
	}
	return ""
}

func parsePSUCfgINI(content []byte) []models.PSU {
	section := ""
	current := make(map[string]string)
	records := make([]map[string]string, 0)
	isPSUSection := func(name string) bool {
		norm := normalizeKey(name)
		return norm == "activestandbyconfiguration" || strings.HasPrefix(norm, "psu")
	}

	flush := func() {
		if len(current) == 0 {
			return
		}
		records = append(records, current)
		current = make(map[string]string)
	}

	scanner := bufio.NewScanner(strings.NewReader(string(content)))
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") {
			continue
		}

		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
			if isPSUSection(section) {
				flush()
			}
			section = strings.TrimSpace(line[1 : len(line)-1])
			continue
		}

		if !isPSUSection(section) {
			continue
		}

		key, value, ok := parseFlexibleKVLine(line)
		if !ok {
			continue
		}
		keyNorm := normalizeKey(key)
		if keyNorm == "powerid" && len(current) > 0 && getSectionValue(current, "Power ID", "PowerID") != "" {
			flush()
		}
		current[key] = value
	}
	if isPSUSection(section) {
		flush()
	}

	out := make([]models.PSU, 0, len(records))
	for _, rec := range records {
		id := strings.TrimSpace(getSectionValue(rec, "Power ID", "PowerID"))
		slot := firstNonEmpty(id, getSectionValue(rec, "Slot", "Position"))
		if slot != "" && !strings.HasPrefix(strings.ToUpper(slot), "PSU") {
			slot = "PSU" + slot
		}

		present := true
		presentRaw := strings.TrimSpace(getSectionValue(rec, "Present Status", "Present"))
		if presentRaw != "" {
			present = containsAny(strings.ToLower(presentRaw), "present", "yes", "ok", "active")
		}

		status := "absent"
		if present {
			status = "ok"
		}
		cold := strings.TrimSpace(getSectionValue(rec, "Cold Status"))
		if strings.Contains(strings.ToLower(cold), "active") {
			status = "ok"
		}

		psu := models.PSU{
			Slot:         slot,
			Present:      present,
			Model:        strings.TrimSpace(getSectionValue(rec, "Model")),
			SerialNumber: strings.TrimSpace(getSectionValue(rec, "SN", "Serial Number")),
			WattageW:     parseMaybeIntLoose(getSectionValue(rec, "Max Power(W)", "Max Power")),
			Status:       status,
			Description:  cold,
		}
		if strings.TrimSpace(psu.Slot) == "" && strings.TrimSpace(psu.SerialNumber) == "" && strings.TrimSpace(psu.Model) == "" {
			continue
		}
		out = append(out, psu)
	}

	return out
}

func parseBoardCfgINI(content []byte, result *models.AnalysisResult) {
	sections := parseLooseINI(string(content))
	if len(sections) == 0 {
		return
	}

	board := &result.Hardware.BoardInfo
	boardType := getSectionValue(findINISection(sections, "Board Type"), "Board Type")
	boardVersion := getSectionValue(findINISection(sections, "Board Version"), "Board Version")
	customerID := getSectionValue(findINISection(sections, "Customer ID"), "CustomerID", "Customer ID")
	oemFlag := getSectionValue(findINISection(sections, "OEM ID"), "OEM Flag", "OEMID")

	setIfEmpty(&board.ProductName, boardType)
	setIfEmpty(&board.Version, boardVersion)

	extras := make([]string, 0, 2)
	if customerID != "" {
		extras = append(extras, "CustomerID: "+customerID)
	}
	if oemFlag != "" {
		extras = append(extras, "OEM Flag: "+oemFlag)
	}
	if len(extras) > 0 {
		if strings.TrimSpace(board.Description) == "" {
			board.Description = strings.Join(extras, "; ")
		} else if !strings.Contains(strings.ToLower(board.Description), strings.ToLower(extras[0])) {
			board.Description = strings.TrimSpace(board.Description) + "; " + strings.Join(extras, "; ")
		}
	}
}

func findINISection(sections map[string]map[string]string, name string) map[string]string {
	target := normalizeKey(name)
	for sectionName, section := range sections {
		if normalizeKey(sectionName) == target {
			return section
		}
	}
	return nil
}

func parseRaidBPConfDevices(content []byte) []models.HardwareDevice {
	lines := strings.Split(string(content), "\n")
	section := ""
	devices := make([]models.HardwareDevice, 0)
	backplaneIndex := 0

	for _, raw := range lines {
		line := strings.TrimSpace(raw)
		if line == "" {
			continue
		}

		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
			section = strings.ToLower(strings.TrimSpace(line[1 : len(line)-1]))
			continue
		}

		switch section {
		case "bp information":
			if strings.Contains(strings.ToLower(line), "description") && strings.Contains(line, "|") {
				continue
			}
			if !strings.Contains(line, "|") {
				continue
			}
			cols := parsePipeColumns(line)
			if len(cols) < 3 {
				continue
			}
			backplaneIndex++
			desc := cols[0]
			bpType := cols[1]
			slot := cols[2]
			if slot == "" || slot == "~" {
				slot = fmt.Sprintf("BP-%d", backplaneIndex)
			}
			dev := models.HardwareDevice{
				ID:          fmt.Sprintf("h3c-bp-%d-%s", backplaneIndex, strings.ToLower(strings.ReplaceAll(slot, " ", "-"))),
				Kind:        models.DeviceKindStorage,
				Slot:        slot,
				Model:       bpType,
				DeviceClass: "backplane",
				Status:      "ok",
				Details: map[string]any{
					"source":      "Raid_BP_Conf_Info.ini",
					"description": desc,
				},
			}
			if len(cols) > 4 && cols[4] != "" && cols[4] != "~" {
				dev.Location = cols[4]
			}
			devices = append(devices, dev)

		case "raid information":
			if strings.Contains(strings.ToLower(line), "pcie slot") && strings.Contains(line, "|") {
				continue
			}
			if !strings.Contains(line, "|") {
				continue
			}
			cols := parsePipeColumns(line)
			if len(cols) < 2 {
				continue
			}
			slot := strings.TrimSpace(cols[0])
			if slot == "" || slot == "~" {
				continue
			}
			sasNum := strings.TrimSpace(cols[1])
			desc := "RAID Controller"
			if sasNum != "" && sasNum != "~" {
				desc = "SAS ports: " + sasNum
			}
			dev := models.HardwareDevice{
				ID:          "h3c-raid-slot-" + slot,
				Kind:        models.DeviceKindPCIe,
				Slot:        "PCIe " + slot,
				Model:       "RAID Controller",
				DeviceClass: "raid_controller",
				Status:      "ok",
				Details: map[string]any{
					"source":      "Raid_BP_Conf_Info.ini",
					"description": desc,
					"pcie_slot":   slot,
					"raid_sasnum": sasNum,
				},
			}
			devices = append(devices, dev)
		}
	}

	return dedupeHardwareDevices(devices)
}

func parsePipeColumns(line string) []string {
	parts := strings.Split(line, "|")
	out := make([]string, 0, len(parts))
	for _, part := range parts {
		v := strings.TrimSpace(part)
		if v == "" {
			continue
		}
		out = append(out, v)
	}
	return out
}

func dedupeHardwareDevices(items []models.HardwareDevice) []models.HardwareDevice {
	out := make([]models.HardwareDevice, 0, len(items))
	seen := make(map[string]struct{}, len(items))
	for _, item := range items {
		key := strings.ToLower(strings.TrimSpace(item.ID))
		if key == "" {
			key = strings.ToLower(strings.TrimSpace(item.Kind + "|" + item.Slot + "|" + item.Model))
		}
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		out = append(out, item)
	}
	return out
}

func parseFirmwareJSON(content []byte, result *models.AnalysisResult, firmwareSeen map[string]struct{}) {
	type fwEntry struct {
		FirmwareName    string `json:"Firmware Name"`
		FirmwareVersion string `json:"Firmware Version"`
		Location        string `json:"Location"`
		PartModel       string `json:"Part Model"`
	}

	var payload map[string]fwEntry
	if err := json.Unmarshal(content, &payload); err != nil {
		return
	}

	keys := make([]string, 0, len(payload))
	for key := range payload {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	for _, key := range keys {
		entry := payload[key]
		version := strings.TrimSpace(entry.FirmwareVersion)
		if version == "" || strings.EqualFold(version, "N/A") {
			continue
		}

		name := strings.TrimSpace(entry.FirmwareName)
		if name == "" {
			name = key
		}
		descParts := make([]string, 0, 2)
		if loc := strings.TrimSpace(entry.Location); loc != "" && loc != "-" {
			descParts = append(descParts, "location: "+loc)
		}
		if part := strings.TrimSpace(entry.PartModel); part != "" && part != "-" {
			descParts = append(descParts, "model: "+part)
		}

		appendFirmwareUnique(result, firmwareSeen, models.FirmwareInfo{
			DeviceName:  name,
			Version:     version,
			Description: strings.Join(descParts, "; "),
		})
	}
}

func parseCPUXML(content []byte, result *models.AnalysisResult) {
	root, ok := parseXMLRoot(content)
	if !ok {
		return
	}

	for _, node := range root.Nodes {
		if !strings.HasPrefix(strings.ToLower(node.XMLName.Local), "cpu") {
			continue
		}

		fields := xmlNodeFields(node)
		model := firstNonEmpty(fields["Model"], fields["CPUName"])
		if model == "" {
			continue
		}

		socket := parseSocketID(node.XMLName.Local)
		cpu := models.CPU{
			Socket:       socket,
			Model:        model,
			Description:  strings.TrimSpace(fields["Manufacturer"]),
			Cores:        parseMaybeInt(fields["TotalCores"]),
			Threads:      parseMaybeInt(fields["TotalThreads"]),
			FrequencyMHz: parseMaybeInt(fields["ProcessorSpeed"]),
			MaxFreqMHz:   parseMaybeInt(fields["ProcessorMaxSpeed"]),
			SerialNumber: strings.TrimSpace(fields["SerialNumber"]),
			PPIN:         strings.TrimSpace(fields["PPIN"]),
			Status:       normalizePresenceStatus(fields["Status"]),
		}

		result.Hardware.CPUs = append(result.Hardware.CPUs, cpu)
	}
}

func parseMemoryXML(content []byte, result *models.AnalysisResult) {
	root, ok := parseXMLRoot(content)
	if !ok {
		return
	}

	for _, node := range root.Nodes {
		name := strings.ToLower(node.XMLName.Local)
		if !strings.HasPrefix(name, "dimm") {
			continue
		}

		fields := xmlNodeFields(node)
		slot := strings.TrimSpace(fields["Name"])
		if slot == "" {
			slot = node.XMLName.Local
		}

		statusRaw := strings.TrimSpace(fields["Status"])
		present := strings.Contains(strings.ToLower(statusRaw), "presence")
		status := "absent"
		if present {
			status = "ok"
		}

		dimm := models.MemoryDIMM{
			Slot:            slot,
			Location:        strings.TrimSpace(fields["DIMMSilk"]),
			Present:         present,
			SizeMB:          parseMaybeInt(fields["DIMMSize"]),
			Type:            strings.TrimSpace(fields["DIMMTech"]),
			MaxSpeedMHz:     parseMaybeInt(fields["MaxFreq"]),
			CurrentSpeedMHz: parseMaybeInt(fields["CurFreq"]),
			SerialNumber:    strings.TrimSpace(fields["SerialNumber"]),
			PartNumber:      strings.TrimSpace(fields["PartNumber"]),
			Status:          status,
			Ranks:           parseMaybeInt(fields["DIMMRanks"]),
		}
		if dimm.Type == "" {
			dimm.Type = "DIMM"
		}

		result.Hardware.Memory = append(result.Hardware.Memory, dimm)
	}
}

var diskSectionRE = regexp.MustCompile(`(?i)^disk_\d+$`)

func parseStorageINI(content []byte) []models.Storage {
	sections := parseLooseINI(string(content))
	names := sortedSectionNames(sections)
	out := make([]models.Storage, 0)

	for _, sectionName := range names {
		if !diskSectionRE.MatchString(strings.TrimSpace(sectionName)) {
			continue
		}

		section := sections[sectionName]
		slot := getSectionValue(section, "DiskSlotDesc")
		if slot == "" {
			slot = sectionName
		}

		present := isTruthy(getSectionValue(section, "Present"))
		status := "absent"
		if present {
			status = "ok"
		}

		storage := models.Storage{
			Slot:         slot,
			SerialNumber: getSectionValue(section, "SerialNumber"),
			Present:      present,
			Status:       status,
			Type:         inferStorageType(slot),
		}
		if storage.Type == "" {
			storage.Type = "disk"
		}

		out = append(out, storage)
	}
	return out
}

func parseRAIDJSONVolumes(content []byte) []models.StorageVolume {
	var root map[string]any
	if err := json.Unmarshal(content, &root); err != nil {
		return nil
	}

	raidCfg := toAnyMap(lookupAnyCase(root, "RaidConfig"))
	ctrlInfo := toAnySlice(lookupAnyCase(raidCfg, "CtrlInfo", "ControllerInfo"))
	volumes := make([]models.StorageVolume, 0)

	for ctrlIdx, ctrlAny := range ctrlInfo {
		ctrl := toAnyMap(ctrlAny)
		ctrlSlot := firstNonEmpty(
			toStringAny(lookupAnyCase(ctrl, "CtrlSlot", "CtrlDevice Slot", "Slot")),
			toStringAny(lookupAnyByPrefix(ctrl, "ctrldeviceslot")),
		)
		ctrlName := firstNonEmpty(
			toStringAny(lookupAnyCase(ctrl, "CtrlName", "CtrlDevice Name", "Name", "ControllerName")),
			toStringAny(lookupAnyByPrefix(ctrl, "ctrldevicename")),
			fmt.Sprintf("RAID Controller %d", ctrlIdx+1),
		)
		controller := ctrlName
		if ctrlSlot != "" {
			controller = fmt.Sprintf("%s (slot %s)", ctrlName, ctrlSlot)
		}

		ldInfo := toAnySlice(lookupAnyCase(ctrl, "LDInfo", "LogicalDeviceInfo", "LogicalDiskInfo", "VirtualDriveInfo"))
		for ldIdx, ldAny := range ldInfo {
			ld := toAnyMap(ldAny)
			vol := parseVolumeFromAnyMap(ld, controller, ldIdx+1)
			if isVolumeEmpty(vol) {
				continue
			}
			volumes = append(volumes, vol)
		}
	}

	return dedupeVolumes(volumes)
}

func parseRAIDDetailTXT(content []byte) ([]models.Storage, []models.StorageVolume) {
	lines := strings.Split(string(content), "\n")

	controllerInfo := make(map[string]string)
	logicalRecords := make([]map[string]string, 0)
	physicalRecords := make([]map[string]string, 0)

	section := ""
	currentLogical := make(map[string]string)
	currentPhysical := make(map[string]string)

	flushLogical := func() {
		if len(currentLogical) == 0 {
			return
		}
		logicalRecords = append(logicalRecords, currentLogical)
		currentLogical = make(map[string]string)
	}
	flushPhysical := func() {
		if len(currentPhysical) == 0 {
			return
		}
		physicalRecords = append(physicalRecords, currentPhysical)
		currentPhysical = make(map[string]string)
	}

	for _, rawLine := range lines {
		line := strings.TrimSpace(rawLine)
		if line == "" {
			continue
		}
		lower := strings.ToLower(line)
		switch {
		case strings.Contains(lower, "controller information"):
			flushLogical()
			flushPhysical()
			section = "controller"
			continue
		case strings.Contains(lower, "logical device information"):
			flushLogical()
			flushPhysical()
			section = "logical"
			continue
		case strings.Contains(lower, "physical device information"):
			flushLogical()
			flushPhysical()
			section = "physical"
			continue
		case strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]"):
			continue
		case strings.HasPrefix(line, "---"):
			continue
		}

		key, value, ok := parseColonKVLine(line)
		if !ok {
			continue
		}
		normKey := normalizeKey(key)

		switch section {
		case "controller":
			controllerInfo[key] = value
		case "logical":
			if len(currentLogical) > 0 && startsNewLogicalRecord(currentLogical, normKey) {
				flushLogical()
			}
			currentLogical[key] = value
		case "physical":
			if len(currentPhysical) > 0 && normKey == "connectionid" {
				flushPhysical()
			}
			currentPhysical[key] = value
		}
	}

	flushLogical()
	flushPhysical()

	controller := firstNonEmpty(
		getSectionValue(controllerInfo, "AssetTag", "CtrlName", "ControllerName", "ChipModel"),
		"RAID Controller",
	)

	storages := make([]models.Storage, 0, len(physicalRecords))
	for _, rec := range physicalRecords {
		slot := firstNonEmpty(getSectionValue(rec, "Position", "Slot", "Location"))
		if slot == "" {
			slot = "Connection " + getSectionValue(rec, "ConnectionID")
		}
		mediaType := getSectionValue(rec, "MediaType", "Type")
		protocol := getSectionValue(rec, "Protocol")
		model := getSectionValue(rec, "Model")
		statusRaw := firstNonEmpty(getSectionValue(rec, "StatusIndicator", "FirmwareStatus", "Status"), "ok")

		capacityBytes, sizeGB := parseCapacityFields(getSectionValue(rec, "CapacityBytes"), getSectionValue(rec, "Capacity"), "")
		if sizeGB == 0 && capacityBytes > 0 {
			sizeGB = int(capacityBytes / 1_000_000_000)
		}

		present := true
		statusLower := strings.ToLower(statusRaw)
		if containsAny(statusLower, "missing", "absent", "removed", "not present") {
			present = false
		}

		storage := models.Storage{
			Slot:         slot,
			Type:         inferStorageTypeFromValues(slot, mediaType, protocol, model),
			Model:        strings.TrimSpace(model),
			SizeGB:       sizeGB,
			SerialNumber: getSectionValue(rec, "SerialNumber"),
			Manufacturer: getSectionValue(rec, "Manufacturer", "Vendor"),
			Firmware:     getSectionValue(rec, "Revision", "FirmwareVersion"),
			Interface:    protocol,
			Present:      present,
			Location:     firstNonEmpty(getSectionValue(rec, "Position"), controller),
			Status:       normalizeStorageStatus(statusRaw, present),
		}
		if storage.Type == "" {
			storage.Type = "disk"
		}
		storages = append(storages, storage)
	}

	volumes := make([]models.StorageVolume, 0, len(logicalRecords))
	for idx, rec := range logicalRecords {
		vol := parseVolumeFromStringMap(rec, controller, idx+1)
		if isVolumeEmpty(vol) {
			continue
		}
		volumes = append(volumes, vol)
	}

	return dedupeStorage(storages), dedupeVolumes(volumes)
}

func parseNVMeInfo(content []byte) []models.Storage {
	text := strings.TrimSpace(string(content))
	if text == "" {
		return nil
	}
	lower := strings.ToLower(text)
	if strings.Contains(lower, "note: no nvme info") {
		return nil
	}

	storages := make([]models.Storage, 0)
	sections := parseLooseINI(text)
	for _, sectionName := range sortedSectionNames(sections) {
		section := sections[sectionName]
		storage := parseStorageFromSection(section, sectionName, "nvme")
		if isStorageEmpty(storage) {
			continue
		}
		storages = append(storages, storage)
	}
	if len(storages) > 0 {
		return dedupeStorage(storages)
	}

	for _, block := range parseKeyValueBlocks(text) {
		storage := parseStorageFromSection(block, "NVMe", "nvme")
		if isStorageEmpty(storage) {
			continue
		}
		storages = append(storages, storage)
	}

	return dedupeStorage(storages)
}

func parseStorageFromSection(section map[string]string, fallbackSlot, defaultType string) models.Storage {
	slot := firstNonEmpty(getSectionValue(section, "DiskSlotDesc", "Position", "Slot", "Name"), fallbackSlot)
	model := firstNonEmpty(getSectionValue(section, "Model", "ProductName", "DeviceModel", "PartNumber"), "")
	media := firstNonEmpty(getSectionValue(section, "MediaType", "Type"), defaultType)
	protocol := firstNonEmpty(getSectionValue(section, "Protocol", "Interface"), "")
	statusRaw := firstNonEmpty(getSectionValue(section, "Status", "State", "Health"), "")
	presentRaw := firstNonEmpty(getSectionValue(section, "Present"), "")

	present := true
	if presentRaw != "" {
		present = isTruthy(presentRaw)
		if !present && !containsAny(strings.ToLower(presentRaw), "absent", "missing", "no") {
			present = true
		}
	}
	if statusRaw != "" && containsAny(strings.ToLower(statusRaw), "absent", "missing", "removed") {
		present = false
	}

	capacityBytes, sizeGB := parseCapacityFields(
		getSectionValue(section, "CapacityBytes", "SizeBytes"),
		getSectionValue(section, "Capacity", "Size"),
		getSectionValue(section, "SizeGB", "CapacityGB"),
	)
	if sizeGB == 0 && capacityBytes > 0 {
		sizeGB = int(capacityBytes / 1_000_000_000)
	}

	storage := models.Storage{
		Slot:         slot,
		Type:         inferStorageTypeFromValues(slot, media, protocol, model),
		Model:        model,
		SizeGB:       sizeGB,
		SerialNumber: getSectionValue(section, "SerialNumber"),
		Manufacturer: getSectionValue(section, "Manufacturer", "Vendor"),
		Firmware:     getSectionValue(section, "Revision", "FirmwareVersion", "Firmware"),
		Interface:    protocol,
		Present:      present,
		Location:     firstNonEmpty(getSectionValue(section, "Position", "Location"), slot),
		Status:       normalizeStorageStatus(statusRaw, present),
	}
	if storage.Type == "" {
		storage.Type = defaultType
	}

	return storage
}

func parseVolumeFromAnyMap(rec map[string]any, controller string, idx int) models.StorageVolume {
	id := firstNonEmpty(
		toStringAny(lookupAnyCase(rec, "LDID", "LD ID", "Id", "ID", "VDID", "LogicalDeviceID", "VirtualDriveID")),
		fmt.Sprintf("ld-%d", idx),
	)
	name := firstNonEmpty(
		toStringAny(lookupAnyCase(rec, "LDName", "LD_name", "Name", "VolumeName", "LogicalDeviceName")),
		id,
	)

	capacityBytes := parseLDCapacity(rec)
	sizeGB := parseMaybeInt(toStringAny(lookupAnyCase(rec, "SizeGB", "CapacityGB")))
	if sizeGB == 0 && capacityBytes > 0 {
		sizeGB = int(capacityBytes / 1_000_000_000)
	}

	bootable, _ := toBoolAny(lookupAnyCase(rec, "Bootable", "IsBootable"))
	encrypted, _ := toBoolAny(lookupAnyCase(rec, "Encrypted", "IsEncrypted", "EncryptionEnabled"))

	return models.StorageVolume{
		ID:            id,
		Name:          name,
		Controller:    controller,
		RAIDLevel:     normalizeRAIDLevel(firstNonEmpty(toStringAny(lookupAnyCase(rec, "RAIDLevel", "RaidLevel", "RAID", "Level")), toStringAny(lookupAnyByPrefix(rec, "raidlevel")))),
		SizeGB:        sizeGB,
		CapacityBytes: capacityBytes,
		Status:        normalizeVolumeStatus(toStringAny(lookupAnyCase(rec, "Status", "State", "HealthStatus"))),
		Bootable:      bootable,
		Encrypted:     encrypted,
	}
}

func parseVolumeFromStringMap(rec map[string]string, controller string, idx int) models.StorageVolume {
	id := firstNonEmpty(
		getSectionValue(rec, "LDID", "Id", "ID", "VDID", "LogicalDeviceID", "VirtualDriveID"),
		fmt.Sprintf("ld-%d", idx),
	)
	name := firstNonEmpty(
		getSectionValue(rec, "LDName", "Name", "VolumeName", "LogicalDeviceName"),
		id,
	)

	capacityBytes, sizeGB := parseCapacityFields(
		getSectionValue(rec, "CapacityBytes", "SizeBytes"),
		getSectionValue(rec, "Capacity", "Size"),
		getSectionValue(rec, "SizeGB", "CapacityGB"),
	)
	if sizeGB == 0 && capacityBytes > 0 {
		sizeGB = int(capacityBytes / 1_000_000_000)
	}

	bootable, _ := parseBoolString(getSectionValue(rec, "Bootable", "IsBootable"))
	encrypted, _ := parseBoolString(getSectionValue(rec, "Encrypted", "IsEncrypted", "EncryptionEnabled"))

	return models.StorageVolume{
		ID:            id,
		Name:          name,
		Controller:    controller,
		RAIDLevel:     normalizeRAIDLevel(getSectionValue(rec, "RAIDLevel", "RaidLevel", "RAID", "Level")),
		SizeGB:        sizeGB,
		CapacityBytes: capacityBytes,
		Status:        normalizeVolumeStatus(getSectionValue(rec, "Status", "State", "HealthStatus")),
		Bootable:      bootable,
		Encrypted:     encrypted,
	}
}

func parseSensorInfoINI(content []byte) []models.SensorReading {
	sensors := make([]models.SensorReading, 0)
	scanner := bufio.NewScanner(strings.NewReader(string(content)))

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || !strings.Contains(line, "|") {
			continue
		}
		if strings.Contains(strings.ToLower(line), "sensor name") && strings.Contains(strings.ToLower(line), "reading") {
			continue
		}

		parts := strings.Split(line, "|")
		if len(parts) < 4 {
			continue
		}

		name := strings.TrimSpace(parts[0])
		reading := strings.TrimSpace(parts[1])
		unit := strings.TrimSpace(parts[2])
		status := strings.TrimSpace(parts[3])
		if name == "" {
			continue
		}

		sensor := models.SensorReading{
			Name:     name,
			Type:     inferSensorType(unit, reading),
			Unit:     unit,
			RawValue: reading,
			Status:   normalizeSensorStatus(status),
		}
		if v, ok := parseMaybeFloat(reading); ok {
			sensor.Value = v
		}

		sensors = append(sensors, sensor)
	}

	return sensors
}

type selEntry struct {
	Created     string `json:"Created"`
	Description string `json:"Description"`
	Severity    string `json:"Severity"`
	EntryCode   string `json:"EntryCode"`
	EntryType   string `json:"EntryType"`
	ID          int    `json:"Id"`
	Level       string `json:"Level"`
	Message     string `json:"Message"`
	SensorName  string `json:"SensorName"`
	SensorType  string `json:"SensorType"`
}

func parseSELJSON(content []byte) []models.Event {
	text := strings.TrimSpace(string(content))
	if text == "" {
		return nil
	}

	if !strings.HasPrefix(text, "[") {
		text = strings.TrimRight(text, ", \n\r\t")
		text = "[" + text + "]"
	}

	var entries []selEntry
	if err := json.Unmarshal([]byte(text), &entries); err != nil {
		return nil
	}

	events := make([]models.Event, 0, len(entries))
	for _, entry := range entries {
		timestamp := parseH3CTimestamp(entry.Created)
		if timestamp.IsZero() {
			timestamp = time.Now()
		}

		description := firstNonEmpty(entry.Message, entry.Description)
		eventType := firstNonEmpty(entry.SensorType, entry.EntryType, "SEL")

		events = append(events, models.Event{
			ID:          strconv.Itoa(entry.ID),
			Timestamp:   timestamp,
			Source:      "SEL",
			SensorType:  strings.TrimSpace(entry.SensorType),
			SensorName:  strings.TrimSpace(entry.SensorName),
			EventType:   strings.TrimSpace(eventType),
			Severity:    mapH3CSeverity(entry.Severity, entry.Level, entry.Message),
			Description: strings.TrimSpace(description),
			RawData:     strings.TrimSpace(entry.EntryCode),
		})
	}

	return events
}

func parseSELListTXT(content []byte) []models.Event {
	lines := strings.Split(string(content), "\n")
	events := make([]models.Event, 0, len(lines))
	location := parser.DefaultArchiveLocation()

	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" || !strings.Contains(line, "|") {
			continue
		}

		parts := strings.Split(line, "|")
		if len(parts) < 6 {
			continue
		}

		id := strings.TrimSpace(parts[0])
		date := strings.TrimSpace(parts[1])
		tod := strings.TrimSpace(parts[2])
		sensor := strings.TrimSpace(parts[3])
		message := strings.TrimSpace(parts[4])
		state := strings.TrimSpace(parts[5])

		if strings.EqualFold(date, "Pre-Init") {
			continue
		}

		ts, err := time.ParseInLocation("01/02/2006 15:04:05", date+" "+tod, location)
		if err != nil {
			continue
		}

		sensorType := strings.TrimSpace(sensor)
		sensorName := ""
		if hashIdx := strings.Index(sensor, "#"); hashIdx != -1 {
			sensorType = strings.TrimSpace(sensor[:hashIdx])
			sensorName = strings.TrimSpace(sensor[hashIdx+1:])
		}

		events = append(events, models.Event{
			ID:          id,
			Timestamp:   ts,
			Source:      "SEL",
			SensorType:  sensorType,
			SensorName:  sensorName,
			EventType:   message,
			Severity:    mapH3CSeverity("", "", sensor+" "+message+" "+state),
			Description: buildSELDescription(message, state),
			RawData:     line,
		})
	}

	return events
}

func parseSELCSVFiles(files []parser.ExtractedFile) []models.Event {
	events := make([]models.Event, 0)
	for _, f := range files {
		path := strings.ToLower(strings.TrimSpace(f.Path))
		if !strings.HasSuffix(path, ".csv") {
			continue
		}
		if !(strings.Contains(path, "/user/") || strings.HasPrefix(path, "user/")) {
			continue
		}
		events = append(events, parseSELCSV(f.Content)...)
	}
	return events
}

func parseSELCSV(content []byte) []models.Event {
	csvText := strings.ReplaceAll(string(content), "\x00", "")
	reader := csv.NewReader(strings.NewReader(csvText))
	reader.FieldsPerRecord = -1
	reader.LazyQuotes = true
	reader.TrimLeadingSpace = true

	header, err := reader.Read()
	if err != nil {
		return nil
	}
	index := make(map[string]int, len(header))
	for i, col := range header {
		index[normalizeKey(col)] = i
	}

	required := []string{"descinfo"}
	if !hasAllKeys(index, required...) {
		return nil
	}
	if !hasAnyKey(index, "recordtimestamp", "eventoccurredtime") {
		return nil
	}

	events := make([]models.Event, 0, 128)

	for {
		row, err := reader.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			continue
		}

		ts := parseSELCSVTimestamp(
			readCSVField(row, index, "eventoccurredtime"),
			readCSVField(row, index, "recordtimestamp"),
		)
		if ts.IsZero() {
			continue
		}

		desc := strings.TrimSpace(readCSVField(row, index, "descinfo"))
		explanation := strings.TrimSpace(readCSVField(row, index, "explanation"))
		if desc == "" && explanation == "" {
			continue
		}

		events = append(events, models.Event{
			ID:          strconv.Itoa(len(events) + 1),
			Timestamp:   ts,
			Source:      "SEL",
			SensorType:  strings.TrimSpace(readCSVField(row, index, "sensortypestr")),
			SensorName:  strings.TrimSpace(readCSVField(row, index, "sensorname")),
			EventType:   strings.TrimSpace(readCSVField(row, index, "eventdir")),
			Severity:    mapH3CSeverity(readCSVField(row, index, "severitylevel"), readCSVField(row, index, "severitylevelid"), desc+" "+explanation),
			Description: composeSELCSVDescription(desc, explanation),
			RawData:     strings.TrimSpace(readCSVField(row, index, "severitylevelid")),
		})
	}

	return events
}

func parseSELCSVTimestamp(values ...string) time.Time {
	location := parser.DefaultArchiveLocation()
	for _, raw := range values {
		clean := strings.TrimSpace(strings.ReplaceAll(raw, "\x00", ""))
		if clean == "" || strings.EqualFold(clean, "Pre-Init") {
			continue
		}
		if ts := parseH3CTimestamp(clean); !ts.IsZero() {
			return ts
		}
		if ts, err := time.ParseInLocation("2006-01-02 15:04:05", clean, location); err == nil {
			return ts
		}
	}
	return time.Time{}
}

func composeSELCSVDescription(desc, explanation string) string {
	desc = strings.TrimSpace(desc)
	explanation = strings.TrimSpace(explanation)
	if explanation == "" || explanation == desc {
		return desc
	}
	if desc == "" {
		return explanation
	}
	return desc + " | " + explanation
}

func readCSVField(row []string, index map[string]int, key string) string {
	i, ok := index[normalizeKey(key)]
	if !ok || i < 0 || i >= len(row) {
		return ""
	}
	return strings.TrimSpace(strings.ReplaceAll(row[i], "\x00", ""))
}

func hasAnyKey(index map[string]int, keys ...string) bool {
	for _, key := range keys {
		if _, ok := index[normalizeKey(key)]; ok {
			return true
		}
	}
	return false
}

func hasAllKeys(index map[string]int, keys ...string) bool {
	for _, key := range keys {
		if _, ok := index[normalizeKey(key)]; !ok {
			return false
		}
	}
	return true
}

func parseH3CTimestamp(value string) time.Time {
	value = strings.TrimSpace(value)
	if value == "" {
		return time.Time{}
	}

	layouts := []string{
		"2006-01-02 15:04:05 MST-07:00",
		"2006-01-02 15:04:05 -07:00",
		time.RFC3339,
	}
	for _, layout := range layouts {
		if ts, err := time.Parse(layout, value); err == nil {
			return ts
		}
	}

	cleaned := strings.ReplaceAll(value, "UTC", "")
	cleaned = strings.Join(strings.Fields(cleaned), " ")
	if ts, err := time.Parse("2006-01-02 15:04:05 -07:00", cleaned); err == nil {
		return ts
	}

	return time.Time{}
}

func mapH3CSeverity(severity, level, text string) models.Severity {
	all := strings.ToLower(strings.TrimSpace(severity + " " + level + " " + text))
	if all == "" {
		return models.SeverityInfo
	}

	if containsAny(all, "critical", "major", "fatal", "unrecoverable", "off-line", "offline", "redundancy lost", "failure", "fault", "error") {
		return models.SeverityCritical
	}
	if containsAny(all, "warning", "warn", "minor", "non-critical", "lost", "degraded", "disabled", "abnormal") {
		return models.SeverityWarning
	}
	return models.SeverityInfo
}

func buildSELDescription(message, state string) string {
	message = strings.TrimSpace(message)
	state = strings.TrimSpace(state)
	if state == "" || strings.EqualFold(state, "Asserted") || strings.EqualFold(state, "Deasserted") {
		return message
	}
	if message == "" {
		return state
	}
	return message + " (" + state + ")"
}

func appendFirmwareUnique(result *models.AnalysisResult, seen map[string]struct{}, fw models.FirmwareInfo) {
	fw.DeviceName = strings.TrimSpace(fw.DeviceName)
	fw.Description = strings.TrimSpace(fw.Description)
	fw.Version = strings.TrimSpace(fw.Version)
	if fw.DeviceName == "" || fw.Version == "" {
		return
	}

	key := strings.ToLower(fw.DeviceName + "|" + fw.Version + "|" + fw.Description)
	if _, ok := seen[key]; ok {
		return
	}
	seen[key] = struct{}{}
	result.Hardware.Firmware = append(result.Hardware.Firmware, fw)
}

func parseLooseINI(content string) map[string]map[string]string {
	sections := make(map[string]map[string]string)
	current := ""
	scanner := bufio.NewScanner(strings.NewReader(content))

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") {
			continue
		}

		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
			current = strings.TrimSpace(line[1 : len(line)-1])
			if current != "" {
				if _, ok := sections[current]; !ok {
					sections[current] = make(map[string]string)
				}
			}
			continue
		}

		if current == "" {
			continue
		}

		sep := strings.IndexAny(line, "=:")
		if sep <= 0 {
			continue
		}
		key := strings.TrimSpace(line[:sep])
		value := strings.TrimSpace(line[sep+1:])
		if key == "" {
			continue
		}
		sections[current][key] = value
	}

	return sections
}

func getSectionValue(section map[string]string, keys ...string) string {
	for _, k := range keys {
		normK := normalizeKey(k)
		for rawKey, rawVal := range section {
			if normalizeKey(rawKey) == normK {
				val := strings.TrimSpace(rawVal)
				if val != "" {
					return val
				}
			}
		}
	}
	return ""
}

func normalizeKey(s string) string {
	s = strings.ToLower(strings.TrimSpace(s))
	var b strings.Builder
	b.Grow(len(s))
	for _, r := range s {
		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
			b.WriteRune(r)
		}
	}
	return b.String()
}

func sortedSectionNames(sections map[string]map[string]string) []string {
	names := make([]string, 0, len(sections))
	for name := range sections {
		names = append(names, name)
	}
	sort.Strings(names)
	return names
}

func isEmptyFRU(f models.FRUInfo) bool {
	return strings.TrimSpace(f.Manufacturer) == "" &&
		strings.TrimSpace(f.ProductName) == "" &&
		strings.TrimSpace(f.SerialNumber) == "" &&
		strings.TrimSpace(f.PartNumber) == "" &&
		strings.TrimSpace(f.Version) == ""
}

func applyBoardFromFRU(board *models.BoardInfo, fru models.FRUInfo) {
	setIfEmpty(&board.Manufacturer, fru.Manufacturer)
	setIfEmpty(&board.ProductName, fru.ProductName)
	setIfEmpty(&board.SerialNumber, fru.SerialNumber)
	setIfEmpty(&board.PartNumber, fru.PartNumber)
	setIfEmpty(&board.Version, fru.Version)
}

func setIfEmpty(dst *string, value string) {
	if strings.TrimSpace(*dst) != "" {
		return
	}
	*dst = strings.TrimSpace(value)
}

type xmlNode struct {
	XMLName xml.Name
	Text    string    `xml:",chardata"`
	Nodes   []xmlNode `xml:",any"`
}

func parseXMLRoot(content []byte) (xmlNode, bool) {
	var root xmlNode
	if err := xml.Unmarshal(content, &root); err != nil {
		return xmlNode{}, false
	}
	return root, true
}

func xmlNodeFields(node xmlNode) map[string]string {
	fields := make(map[string]string, len(node.Nodes))
	for _, child := range node.Nodes {
		fields[child.XMLName.Local] = strings.TrimSpace(child.Text)
	}
	return fields
}

var socketSuffixRE = regexp.MustCompile(`(?i)cpu(\d+)$`)

func parseSocketID(name string) int {
	m := socketSuffixRE.FindStringSubmatch(strings.TrimSpace(name))
	if len(m) != 2 {
		return 0
	}
	v, _ := strconv.Atoi(m[1])
	if v <= 0 {
		return 0
	}
	return v
}

func parseSectionIndex(sectionName, expr string) int {
	re, err := regexp.Compile(expr)
	if err != nil {
		return 0
	}
	match := re.FindStringSubmatch(sectionName)
	if len(match) < 2 {
		return 0
	}
	v, _ := strconv.Atoi(match[1])
	if v <= 0 {
		return 0
	}
	return v
}

func formatSlot(frontRear, slotNum string) string {
	frontRear = strings.TrimSpace(frontRear)
	slotNum = strings.TrimSpace(slotNum)
	switch {
	case frontRear == "" && slotNum == "":
		return ""
	case frontRear == "":
		return slotNum
	case slotNum == "":
		return frontRear
	default:
		return frontRear + " slot " + slotNum
	}
}

func parseMaybeInt(raw string) int {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return 0
	}

	if strings.HasPrefix(strings.ToLower(raw), "0x") {
		if v, err := strconv.ParseInt(raw[2:], 16, 64); err == nil {
			return int(v)
		}
	}

	if v, err := strconv.Atoi(raw); err == nil {
		return v
	}
	if fv, err := strconv.ParseFloat(raw, 64); err == nil {
		return int(fv)
	}
	return 0
}

var firstNumberRE = regexp.MustCompile(`[-+]?[0-9]+(?:\.[0-9]+)?`)

func parseMaybeIntLoose(raw string) int {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return 0
	}
	if v := parseMaybeInt(raw); v > 0 {
		return v
	}

	number := firstNumberRE.FindString(raw)
	if number == "" {
		return 0
	}
	if fv, err := strconv.ParseFloat(number, 64); err == nil {
		return int(fv)
	}
	return 0
}

func parseMaybeFloat(raw string) (float64, bool) {
	raw = strings.TrimSpace(raw)
	if raw == "" || strings.EqualFold(raw, "na") {
		return 0, false
	}
	if strings.HasPrefix(strings.ToLower(raw), "0x") {
		return 0, false
	}
	v, err := strconv.ParseFloat(raw, 64)
	if err != nil {
		return 0, false
	}
	return v, true
}

func normalizePresenceStatus(raw string) string {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return ""
	}
	if strings.Contains(strings.ToLower(raw), "presence") {
		return "ok"
	}
	return strings.ToLower(raw)
}

func normalizeComponentStatus(raw string) string {
	s := strings.ToLower(strings.TrimSpace(raw))
	switch {
	case s == "":
		return ""
	case containsAny(s, "ok", "normal", "presence", "present", "healthy", "running"):
		return "ok"
	case containsAny(s, "absent", "missing", "not present", "removed"):
		return "absent"
	case containsAny(s, "warn", "degrad", "minor"):
		return "warning"
	case containsAny(s, "critical", "major", "fault", "fail", "error"):
		return "critical"
	default:
		return s
	}
}

func inferStorageType(slot string) string {
	return inferStorageTypeFromValues(slot, "", "", "")
}

func inferStorageTypeFromValues(slot, mediaType, protocol, model string) string {
	lower := strings.ToLower(strings.TrimSpace(slot + " " + mediaType + " " + protocol + " " + model))
	switch {
	case strings.Contains(lower, "nvme"):
		return "nvme"
	case strings.Contains(lower, "ssd"):
		return "ssd"
	case strings.Contains(lower, "hdd"), strings.Contains(lower, "sas"), strings.Contains(lower, "sata"):
		return "disk"
	case strings.Contains(lower, "disk"), strings.Contains(lower, "front"), strings.Contains(lower, "rear"):
		return "disk"
	default:
		return ""
	}
}

func inferSensorType(unit, reading string) string {
	lowerUnit := strings.ToLower(strings.TrimSpace(unit))
	switch {
	case strings.Contains(lowerUnit, "degrees"):
		return "temperature"
	case strings.Contains(lowerUnit, "volts"):
		return "voltage"
	case strings.Contains(lowerUnit, "amps"):
		return "current"
	case strings.Contains(lowerUnit, "watts"):
		return "power"
	case strings.Contains(lowerUnit, "rpm"):
		return "fan"
	case strings.Contains(lowerUnit, "discrete"):
		return "discrete"
	}

	if strings.HasPrefix(strings.ToLower(strings.TrimSpace(reading)), "0x") {
		return "discrete"
	}
	return "sensor"
}

func normalizeSensorStatus(status string) string {
	status = strings.TrimSpace(status)
	if status == "" {
		return "unknown"
	}
	if strings.EqualFold(status, "na") {
		return "unknown"
	}
	return strings.ToLower(status)
}

func isTruthy(raw string) bool {
	raw = strings.ToLower(strings.TrimSpace(raw))
	return raw == "yes" || raw == "y" || raw == "true" || raw == "1" || raw == "present"
}

func containsAny(s string, items ...string) bool {
	for _, item := range items {
		if strings.Contains(s, item) {
			return true
		}
	}
	return false
}

func normalizeStorageStatus(raw string, present bool) string {
	s := strings.ToLower(strings.TrimSpace(raw))
	switch {
	case s == "" && present:
		return "ok"
	case s == "":
		return "absent"
	case containsAny(s, "ok", "healthy", "optimal", "normal"):
		return "ok"
	case containsAny(s, "missing", "absent", "removed", "not present"):
		return "absent"
	case containsAny(s, "predictive", "degrad", "warn"):
		return "warning"
	default:
		return s
	}
}

func normalizeVolumeStatus(raw string) string {
	s := strings.ToLower(strings.TrimSpace(raw))
	switch {
	case s == "":
		return ""
	case containsAny(s, "ok", "healthy", "optimal", "normal"):
		return "ok"
	case containsAny(s, "degrad", "rebuild", "warn"):
		return "warning"
	case containsAny(s, "fail", "offline", "critical"):
		return "critical"
	default:
		return s
	}
}

func normalizeRAIDLevel(raw string) string {
	s := strings.ToUpper(strings.TrimSpace(raw))
	s = strings.ReplaceAll(s, " ", "")
	if s == "" {
		return ""
	}
	if strings.HasPrefix(s, "RAID") {
		return s
	}
	if raidNumericRE.MatchString(s) {
		return "RAID" + s
	}
	return s
}

var raidNumericRE = regexp.MustCompile(`^\d+(?:\+\d+)?$`)
var sizeValueRE = regexp.MustCompile(`(?i)^([0-9]+(?:\.[0-9]+)?)\s*([KMGTPE]?)(I?B)?$`)

func parseCapacityFields(bytesField, sizeField, sizeGBField string) (int64, int) {
	if b := parseCapacityFromString(bytesField); b > 0 {
		return b, int(b / 1_000_000_000)
	}
	if b := parseCapacityFromString(sizeField); b > 0 {
		return b, int(b / 1_000_000_000)
	}
	sizeGB := parseMaybeInt(sizeGBField)
	if sizeGB > 0 {
		return int64(sizeGB) * 1_000_000_000, sizeGB
	}
	return 0, 0
}

func parseCapacityFromString(raw string) int64 {
	clean := strings.ReplaceAll(strings.TrimSpace(raw), ",", "")
	if clean == "" {
		return 0
	}
	if strings.EqualFold(clean, "na") {
		return 0
	}
	if v, err := strconv.ParseInt(clean, 10, 64); err == nil {
		return v
	}
	m := sizeValueRE.FindStringSubmatch(clean)
	if len(m) != 4 {
		return 0
	}
	value, err := strconv.ParseFloat(m[1], 64)
	if err != nil {
		return 0
	}
	unit := strings.ToUpper(m[2])
	suffix := strings.ToUpper(m[3])
	base := float64(1000)
	if strings.HasPrefix(suffix, "I") {
		base = 1024
	}
	power := 0
	switch unit {
	case "K":
		power = 1
	case "M":
		power = 2
	case "G":
		power = 3
	case "T":
		power = 4
	case "P":
		power = 5
	case "E":
		power = 6
	}
	mult := float64(1)
	for i := 0; i < power; i++ {
		mult *= base
	}
	return int64(value * mult)
}

func parseCapacityFromAny(v any) int64 {
	switch t := v.(type) {
	case nil:
		return 0
	case float64:
		return int64(t)
	case int:
		return int64(t)
	case int64:
		return t
	case json.Number:
		if i, err := t.Int64(); err == nil {
			return i
		}
		if f, err := t.Float64(); err == nil {
			return int64(f)
		}
		return 0
	case string:
		return parseCapacityFromString(t)
	case map[string]any:
		return parseCapacityFromAny(lookupAnyCase(t, "value", "Value", "CapacityBytes", "SizeBytes"))
	default:
		return parseCapacityFromString(fmt.Sprintf("%v", t))
	}
}

func parseLDCapacity(rec map[string]any) int64 {
	base := parseCapacityFromAny(
		lookupAnyCase(rec, "CapacityBytes", "SizeBytes", "Capacity", "Size", "LogicalCapacity", "LogicalCapacityBytes"),
	)
	if base > 0 {
		return base
	}

	for rawKey, rawVal := range rec {
		norm := normalizeKey(rawKey)
		if strings.HasPrefix(norm, "logicalcapacity") || strings.HasPrefix(norm, "logicalcapicity") {
			v := parseCapacityFromAny(rawVal)
			if v <= 0 {
				continue
			}
			if strings.Contains(norm, "512") {
				return v * 512
			}
			return v
		}
	}
	return 0
}

func parseColonKVLine(line string) (string, string, bool) {
	idx := strings.Index(line, ":")
	if idx <= 0 {
		return "", "", false
	}
	key := strings.TrimSpace(line[:idx])
	value := strings.TrimSpace(line[idx+1:])
	if key == "" {
		return "", "", false
	}
	return key, value, true
}

func parseFlexibleKVLine(line string) (string, string, bool) {
	if key, val, ok := parseColonKVLine(line); ok {
		return key, val, true
	}
	idx := strings.Index(line, "=")
	if idx <= 0 {
		return "", "", false
	}
	key := strings.TrimSpace(line[:idx])
	value := strings.TrimSpace(line[idx+1:])
	if key == "" {
		return "", "", false
	}
	return key, value, true
}

func startsNewLogicalRecord(current map[string]string, normalizedKey string) bool {
	switch normalizedKey {
	case "ldid", "logicaldeviceid", "virtualdriveid":
		return true
	case "id":
		return hasAnyNormalizedKey(current, "ldid", "logicaldeviceid", "virtualdriveid", "id")
	case "name":
		return hasAnyNormalizedKey(current, "name", "ldname", "logicaldevicename")
	default:
		return false
	}
}

func hasAnyNormalizedKey(m map[string]string, keys ...string) bool {
	for rawKey := range m {
		n := normalizeKey(rawKey)
		for _, key := range keys {
			if n == key {
				return true
			}
		}
	}
	return false
}

func parseKeyValueBlocks(content string) []map[string]string {
	blocks := make([]map[string]string, 0)
	current := make(map[string]string)

	flush := func() {
		if len(current) == 0 {
			return
		}
		blocks = append(blocks, current)
		current = make(map[string]string)
	}

	lines := strings.Split(content, "\n")
	for _, rawLine := range lines {
		line := strings.TrimSpace(rawLine)
		if line == "" {
			flush()
			continue
		}
		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
			flush()
			continue
		}
		key, value, ok := parseFlexibleKVLine(line)
		if !ok {
			continue
		}
		nk := normalizeKey(key)
		if nk == "serialnumber" && hasAnyNormalizedKey(current, "serialnumber") {
			flush()
		}
		current[key] = value
	}
	flush()
	return blocks
}

func findCPUIndex(items []models.CPU, target models.CPU) int {
	targetSocket := target.Socket
	targetPPIN := strings.ToLower(strings.TrimSpace(target.PPIN))
	targetSerial := strings.ToLower(strings.TrimSpace(target.SerialNumber))
	targetModel := strings.ToLower(strings.TrimSpace(target.Model))

	for i := range items {
		cpu := items[i]
		if targetSocket > 0 && cpu.Socket > 0 && targetSocket == cpu.Socket {
			return i
		}
		if targetSocket > 0 && cpu.Socket > 0 && targetSocket != cpu.Socket {
			continue
		}

		ppin := strings.ToLower(strings.TrimSpace(cpu.PPIN))
		if targetPPIN != "" && ppin != "" && targetPPIN == ppin {
			return i
		}

		serial := strings.ToLower(strings.TrimSpace(cpu.SerialNumber))
		if targetSerial != "" && serial != "" && targetSerial == serial {
			return i
		}

		model := strings.ToLower(strings.TrimSpace(cpu.Model))
		if targetSocket == 0 && cpu.Socket == 0 && targetModel != "" && model == targetModel {
			return i
		}
	}
	return -1
}

func mergeCPU(dst *models.CPU, src models.CPU) {
	if dst.Socket == 0 && src.Socket > 0 {
		dst.Socket = src.Socket
	}
	setStorageString(&dst.Model, src.Model)
	setStorageString(&dst.Description, src.Description)
	if dst.Cores == 0 && src.Cores > 0 {
		dst.Cores = src.Cores
	}
	if dst.Threads == 0 && src.Threads > 0 {
		dst.Threads = src.Threads
	}
	if dst.FrequencyMHz == 0 && src.FrequencyMHz > 0 {
		dst.FrequencyMHz = src.FrequencyMHz
	}
	if dst.MaxFreqMHz == 0 && src.MaxFreqMHz > 0 {
		dst.MaxFreqMHz = src.MaxFreqMHz
	}
	if dst.L1CacheKB == 0 && src.L1CacheKB > 0 {
		dst.L1CacheKB = src.L1CacheKB
	}
	if dst.L2CacheKB == 0 && src.L2CacheKB > 0 {
		dst.L2CacheKB = src.L2CacheKB
	}
	if dst.L3CacheKB == 0 && src.L3CacheKB > 0 {
		dst.L3CacheKB = src.L3CacheKB
	}
	setStorageString(&dst.PPIN, src.PPIN)
	setStorageString(&dst.SerialNumber, src.SerialNumber)
	setStorageString(&dst.Status, src.Status)
}

func findMemoryIndex(items []models.MemoryDIMM, target models.MemoryDIMM) int {
	targetSerial := strings.ToLower(strings.TrimSpace(target.SerialNumber))
	targetSlot := strings.ToLower(strings.TrimSpace(target.Slot))
	for i := range items {
		serial := strings.ToLower(strings.TrimSpace(items[i].SerialNumber))
		slot := strings.ToLower(strings.TrimSpace(items[i].Slot))
		if targetSerial != "" && serial != "" && targetSerial == serial {
			return i
		}
		if targetSerial == "" && targetSlot != "" && slot != "" && targetSlot == slot {
			return i
		}
	}
	return -1
}

func mergeMemoryDIMM(dst *models.MemoryDIMM, src models.MemoryDIMM) {
	setStorageString(&dst.Slot, src.Slot)
	setStorageString(&dst.Location, src.Location)
	if src.Present {
		dst.Present = true
	}
	if dst.SizeMB == 0 && src.SizeMB > 0 {
		dst.SizeMB = src.SizeMB
	}
	setStorageString(&dst.Type, src.Type)
	setStorageString(&dst.Technology, src.Technology)
	if dst.MaxSpeedMHz == 0 && src.MaxSpeedMHz > 0 {
		dst.MaxSpeedMHz = src.MaxSpeedMHz
	}
	if dst.CurrentSpeedMHz == 0 && src.CurrentSpeedMHz > 0 {
		dst.CurrentSpeedMHz = src.CurrentSpeedMHz
	}
	setStorageString(&dst.Manufacturer, src.Manufacturer)
	setStorageString(&dst.SerialNumber, src.SerialNumber)
	setStorageString(&dst.PartNumber, src.PartNumber)
	setStorageString(&dst.Status, src.Status)
	if dst.Ranks == 0 && src.Ranks > 0 {
		dst.Ranks = src.Ranks
	}
}

func appendUniqueStorages(dst *[]models.Storage, additions []models.Storage) {
	for _, add := range additions {
		if isStorageEmpty(add) {
			continue
		}
		idx := findStorageIndex(*dst, add)
		if idx < 0 {
			*dst = append(*dst, add)
			continue
		}
		mergeStorage(&(*dst)[idx], add)
	}
}

func dedupeStorage(items []models.Storage) []models.Storage {
	out := make([]models.Storage, 0, len(items))
	appendUniqueStorages(&out, items)
	return out
}

func findStorageIndex(items []models.Storage, target models.Storage) int {
	targetSerial := strings.ToLower(strings.TrimSpace(target.SerialNumber))
	targetSlot := strings.ToLower(strings.TrimSpace(target.Slot))
	for i := range items {
		serial := strings.ToLower(strings.TrimSpace(items[i].SerialNumber))
		slot := strings.ToLower(strings.TrimSpace(items[i].Slot))
		if targetSerial != "" && serial != "" && targetSerial == serial {
			return i
		}
		if targetSerial == "" && targetSlot != "" && slot != "" && targetSlot == slot {
			return i
		}
	}
	return -1
}

func mergeStorage(dst *models.Storage, src models.Storage) {
	setStorageString(&dst.Slot, src.Slot)
	if mergedType := strings.TrimSpace(pickStorageType(dst.Type, src.Type)); mergedType != "" {
		dst.Type = mergedType
	}
	setStorageString(&dst.Model, src.Model)
	if dst.SizeGB == 0 && src.SizeGB > 0 {
		dst.SizeGB = src.SizeGB
	}
	setStorageString(&dst.SerialNumber, src.SerialNumber)
	setStorageString(&dst.Manufacturer, src.Manufacturer)
	setStorageString(&dst.Firmware, src.Firmware)
	setStorageString(&dst.Interface, src.Interface)
	if src.Present {
		dst.Present = true
	}
	setStorageString(&dst.Location, src.Location)
	setStorageString(&dst.Status, normalizeStorageStatus(src.Status, src.Present || dst.Present))
	dst.Details = mergeH3CDetails(dst.Details, src.Details)
}

func setStorageString(dst *string, value string) {
	value = strings.TrimSpace(value)
	if value == "" {
		return
	}
	if strings.TrimSpace(*dst) == "" {
		*dst = value
		return
	}
	if strings.EqualFold(*dst, "unknown") || strings.EqualFold(*dst, "n/a") || strings.EqualFold(*dst, "absent") {
		*dst = value
	}
}

func pickStorageType(current, next string) string {
	if storageTypeRank(next) > storageTypeRank(current) {
		return next
	}
	if strings.TrimSpace(current) == "" {
		return next
	}
	return current
}

func storageTypeRank(t string) int {
	switch strings.ToLower(strings.TrimSpace(t)) {
	case "nvme":
		return 4
	case "ssd":
		return 3
	case "hdd", "disk":
		return 2
	case "unknown":
		return 0
	default:
		if strings.TrimSpace(t) == "" {
			return 0
		}
		return 1
	}
}

func isStorageEmpty(s models.Storage) bool {
	return strings.TrimSpace(s.Slot) == "" &&
		strings.TrimSpace(s.SerialNumber) == "" &&
		strings.TrimSpace(s.Model) == ""
}

func appendUniqueNetworkAdapters(dst *[]models.NetworkAdapter, additions []models.NetworkAdapter) {
	for _, add := range additions {
		if isNetworkAdapterEmpty(add) {
			continue
		}
		idx := findNetworkAdapterIndex(*dst, add)
		if idx < 0 {
			*dst = append(*dst, add)
			continue
		}
		mergeNetworkAdapter(&(*dst)[idx], add)
	}
}

func isNetworkAdapterEmpty(n models.NetworkAdapter) bool {
	return strings.TrimSpace(n.Slot) == "" &&
		len(n.MACAddresses) == 0 &&
		strings.TrimSpace(n.Model) == ""
}

func findNetworkAdapterIndex(items []models.NetworkAdapter, target models.NetworkAdapter) int {
	targetSlot := strings.ToLower(strings.TrimSpace(target.Slot))
	for i := range items {
		if hasSharedMAC(items[i].MACAddresses, target.MACAddresses) {
			return i
		}
		slot := strings.ToLower(strings.TrimSpace(items[i].Slot))
		if targetSlot != "" && slot != "" && targetSlot == slot {
			return i
		}
	}
	return -1
}

func hasSharedMAC(a, b []string) bool {
	if len(a) == 0 || len(b) == 0 {
		return false
	}
	set := make(map[string]struct{}, len(a))
	for _, item := range a {
		norm := strings.ToLower(strings.TrimSpace(item))
		if norm != "" {
			set[norm] = struct{}{}
		}
	}
	for _, item := range b {
		norm := strings.ToLower(strings.TrimSpace(item))
		if norm == "" {
			continue
		}
		if _, ok := set[norm]; ok {
			return true
		}
	}
	return false
}

func mergeNetworkAdapter(dst *models.NetworkAdapter, src models.NetworkAdapter) {
	setStorageString(&dst.Slot, src.Slot)
	setStorageString(&dst.Location, src.Location)
	if src.Present {
		dst.Present = true
	}
	setStorageString(&dst.Model, src.Model)
	dst.Description = mergeTextUnique(dst.Description, src.Description)
	setStorageString(&dst.Vendor, src.Vendor)
	if dst.VendorID == 0 && src.VendorID != 0 {
		dst.VendorID = src.VendorID
	}
	if dst.DeviceID == 0 && src.DeviceID != 0 {
		dst.DeviceID = src.DeviceID
	}
	setStorageString(&dst.SerialNumber, src.SerialNumber)
	setStorageString(&dst.PartNumber, src.PartNumber)
	setStorageString(&dst.Firmware, src.Firmware)
	dst.PortCount = maxInt(dst.PortCount, src.PortCount)
	setStorageString(&dst.PortType, src.PortType)
	for _, mac := range src.MACAddresses {
		appendStringUnique(&dst.MACAddresses, mac)
	}
	dst.PortCount = maxInt(dst.PortCount, len(dst.MACAddresses))
	setStorageString(&dst.Status, src.Status)
}

func appendUniqueNICs(dst *[]models.NIC, additions []models.NIC) {
	for _, add := range additions {
		if isNICEmpty(add) {
			continue
		}
		idx := findNICIndex(*dst, add)
		if idx < 0 {
			*dst = append(*dst, add)
			continue
		}
		mergeNIC(&(*dst)[idx], add)
	}
}

func isNICEmpty(n models.NIC) bool {
	return strings.TrimSpace(n.Name) == "" &&
		strings.TrimSpace(n.Model) == "" &&
		strings.TrimSpace(n.MACAddress) == ""
}

func findNICIndex(items []models.NIC, target models.NIC) int {
	targetMAC := strings.ToLower(strings.TrimSpace(target.MACAddress))
	targetName := strings.ToLower(strings.TrimSpace(target.Name))
	for i := range items {
		mac := strings.ToLower(strings.TrimSpace(items[i].MACAddress))
		if targetMAC != "" && mac != "" && targetMAC == mac {
			return i
		}
		name := strings.ToLower(strings.TrimSpace(items[i].Name))
		if targetMAC == "" && targetName != "" && name == targetName {
			return i
		}
	}
	return -1
}

func mergeNIC(dst *models.NIC, src models.NIC) {
	setStorageString(&dst.Name, src.Name)
	setStorageString(&dst.Model, src.Model)
	dst.Description = mergeTextUnique(dst.Description, src.Description)
	setStorageString(&dst.MACAddress, src.MACAddress)
	if dst.SpeedMbps == 0 && src.SpeedMbps > 0 {
		dst.SpeedMbps = src.SpeedMbps
	}
	setStorageString(&dst.SerialNumber, src.SerialNumber)
}

func mergeTextUnique(dst, src string) string {
	dst = strings.TrimSpace(dst)
	src = strings.TrimSpace(src)
	switch {
	case dst == "":
		return src
	case src == "":
		return dst
	case strings.EqualFold(dst, src):
		return dst
	case strings.Contains(strings.ToLower(dst), strings.ToLower(src)):
		return dst
	case strings.Contains(strings.ToLower(src), strings.ToLower(dst)):
		return src
	default:
		return dst + "; " + src
	}
}

func appendUniquePSUs(dst *[]models.PSU, additions []models.PSU) {
	for _, add := range additions {
		if isPSUEmpty(add) {
			continue
		}
		idx := findPSUIndex(*dst, add)
		if idx < 0 {
			*dst = append(*dst, add)
			continue
		}
		mergePSU(&(*dst)[idx], add)
	}
}

func isPSUEmpty(p models.PSU) bool {
	return strings.TrimSpace(p.Slot) == "" &&
		strings.TrimSpace(p.SerialNumber) == "" &&
		strings.TrimSpace(p.Model) == ""
}

func findPSUIndex(items []models.PSU, target models.PSU) int {
	targetSerial := strings.ToLower(strings.TrimSpace(target.SerialNumber))
	targetSlot := strings.ToLower(strings.TrimSpace(target.Slot))
	for i := range items {
		serial := strings.ToLower(strings.TrimSpace(items[i].SerialNumber))
		slot := strings.ToLower(strings.TrimSpace(items[i].Slot))
		if targetSerial != "" && serial != "" && targetSerial == serial {
			return i
		}
		if targetSerial == "" && targetSlot != "" && slot != "" && targetSlot == slot {
			return i
		}
	}
	return -1
}

func mergePSU(dst *models.PSU, src models.PSU) {
	setStorageString(&dst.Slot, src.Slot)
	if src.Present {
		dst.Present = true
	}
	setStorageString(&dst.Model, src.Model)
	setStorageString(&dst.Description, src.Description)
	setStorageString(&dst.Vendor, src.Vendor)
	if dst.WattageW == 0 && src.WattageW > 0 {
		dst.WattageW = src.WattageW
	}
	setStorageString(&dst.SerialNumber, src.SerialNumber)
	setStorageString(&dst.PartNumber, src.PartNumber)
	setStorageString(&dst.Firmware, src.Firmware)
	setStorageString(&dst.Status, src.Status)
	dst.Details = mergeH3CDetails(dst.Details, src.Details)
}

func mergeH3CDetails(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 key, value := range secondary {
		if _, ok := primary[key]; !ok {
			primary[key] = value
		}
	}
	return primary
}

func dedupeVolumes(items []models.StorageVolume) []models.StorageVolume {
	out := make([]models.StorageVolume, 0, len(items))
	for _, item := range items {
		if isVolumeEmpty(item) {
			continue
		}
		idx := findVolumeIndex(out, item)
		if idx < 0 {
			out = append(out, item)
			continue
		}
		mergeVolume(&out[idx], item)
	}
	return out
}

func findVolumeIndex(items []models.StorageVolume, target models.StorageVolume) int {
	targetID := strings.ToLower(strings.TrimSpace(target.ID))
	targetName := strings.ToLower(strings.TrimSpace(target.Name))
	targetController := strings.ToLower(strings.TrimSpace(target.Controller))
	for i := range items {
		id := strings.ToLower(strings.TrimSpace(items[i].ID))
		name := strings.ToLower(strings.TrimSpace(items[i].Name))
		controller := strings.ToLower(strings.TrimSpace(items[i].Controller))
		if targetID != "" && id != "" && targetID == id {
			if !controllersCompatible(targetController, controller) {
				continue
			}
			return i
		}
		if targetName != "" && name == targetName {
			if controllersCompatible(targetController, controller) {
				return i
			}
		}
		if targetID == "" && targetName != "" && name == targetName && controller == targetController {
			return i
		}
	}
	return -1
}

func controllersCompatible(a, b string) bool {
	a = strings.ToLower(strings.TrimSpace(a))
	b = strings.ToLower(strings.TrimSpace(b))
	if a == "" || b == "" {
		return true
	}
	if a == b {
		return true
	}
	if strings.Contains(a, b) || strings.Contains(b, a) {
		return true
	}
	return false
}

func mergeVolume(dst *models.StorageVolume, src models.StorageVolume) {
	setStorageString(&dst.ID, src.ID)
	setStorageString(&dst.Name, src.Name)
	setStorageString(&dst.Controller, src.Controller)
	setStorageString(&dst.RAIDLevel, src.RAIDLevel)
	if dst.SizeGB == 0 && src.SizeGB > 0 {
		dst.SizeGB = src.SizeGB
	}
	if dst.CapacityBytes == 0 && src.CapacityBytes > 0 {
		dst.CapacityBytes = src.CapacityBytes
	}
	setStorageString(&dst.Status, src.Status)
	if src.Bootable {
		dst.Bootable = true
	}
	if src.Encrypted {
		dst.Encrypted = true
	}
}

func isVolumeEmpty(v models.StorageVolume) bool {
	return strings.TrimSpace(v.ID) == "" &&
		strings.TrimSpace(v.Name) == "" &&
		strings.TrimSpace(v.RAIDLevel) == "" &&
		v.CapacityBytes == 0 &&
		v.SizeGB == 0
}

func lookupAnyCase(m map[string]any, keys ...string) any {
	if len(m) == 0 {
		return nil
	}
	for _, key := range keys {
		norm := normalizeKey(key)
		for rawKey, value := range m {
			if normalizeKey(rawKey) == norm {
				return value
			}
		}
	}
	return nil
}

func lookupAnyByPrefix(m map[string]any, prefixes ...string) any {
	if len(m) == 0 {
		return nil
	}
	normPrefixes := make([]string, 0, len(prefixes))
	for _, prefix := range prefixes {
		n := normalizeKey(prefix)
		if n != "" {
			normPrefixes = append(normPrefixes, n)
		}
	}
	for rawKey, value := range m {
		normKey := normalizeKey(rawKey)
		for _, prefix := range normPrefixes {
			if strings.HasPrefix(normKey, prefix) {
				return value
			}
		}
	}
	return nil
}

func toAnyMap(v any) map[string]any {
	if v == nil {
		return nil
	}
	if m, ok := v.(map[string]any); ok {
		return m
	}
	return nil
}

func toAnySlice(v any) []any {
	if v == nil {
		return nil
	}
	switch t := v.(type) {
	case []any:
		return t
	case []map[string]any:
		out := make([]any, 0, len(t))
		for _, item := range t {
			out = append(out, item)
		}
		return out
	default:
		return nil
	}
}

func toStringAny(v any) string {
	switch t := v.(type) {
	case nil:
		return ""
	case string:
		return strings.TrimSpace(t)
	case fmt.Stringer:
		return strings.TrimSpace(t.String())
	case float64:
		return strconv.FormatInt(int64(t), 10)
	case int:
		return strconv.Itoa(t)
	case int64:
		return strconv.FormatInt(t, 10)
	case bool:
		if t {
			return "true"
		}
		return "false"
	case json.Number:
		return t.String()
	default:
		return strings.TrimSpace(fmt.Sprintf("%v", t))
	}
}

func toBoolAny(v any) (bool, bool) {
	switch t := v.(type) {
	case nil:
		return false, false
	case bool:
		return t, true
	case string:
		return parseBoolString(t)
	case float64:
		return t != 0, true
	case int:
		return t != 0, true
	case int64:
		return t != 0, true
	case json.Number:
		if i, err := t.Int64(); err == nil {
			return i != 0, true
		}
		if f, err := t.Float64(); err == nil {
			return f != 0, true
		}
		return false, false
	default:
		return parseBoolString(fmt.Sprintf("%v", t))
	}
}

func parseBoolString(raw string) (bool, bool) {
	switch strings.ToLower(strings.TrimSpace(raw)) {
	case "1", "true", "yes", "y", "enabled", "on":
		return true, true
	case "0", "false", "no", "n", "disabled", "off":
		return false, true
	default:
		return false, false
	}
}

func firstNonEmpty(values ...string) string {
	for _, v := range values {
		v = strings.TrimSpace(v)
		if v != "" {
			return v
		}
	}
	return ""
}

func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func maxInt(a, b int) int {
	if a > b {
		return a
	}
	return b
}
