package collector

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"
	"time"

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

func testAnalysisPlan(d redfishprofile.AnalysisDirectives) redfishprofile.ResolvedAnalysisPlan {
	return redfishprofile.ResolvedAnalysisPlan{Directives: d}
}

func TestRedfishConnectorCollect(t *testing.T) {
	mux := http.NewServeMux()
	register := func(path string, payload interface{}) {
		mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			_ = json.NewEncoder(w).Encode(payload)
		})
	}

	register("/redfish/v1", map[string]interface{}{"Name": "ServiceRoot"})
	register("/redfish/v1/Systems/1", map[string]interface{}{
		"Manufacturer": "Supermicro",
		"Model":        "SYS-TEST",
		"SerialNumber": "SYS123",
		"BiosVersion":  "2.1a",
	})
	register("/redfish/v1/Systems/1/Bios", map[string]interface{}{"Version": "2.1a"})
	register("/redfish/v1/Systems/1/SecureBoot", map[string]interface{}{"SecureBootCurrentBoot": "Enabled"})
	register("/redfish/v1/Systems/1/Processors", map[string]interface{}{
		"Members": []map[string]string{
			{"@odata.id": "/redfish/v1/Systems/1/Processors/CPU1"},
		},
	})
	register("/redfish/v1/Systems/1/Processors/CPU1", map[string]interface{}{
		"Name":         "CPU1",
		"Model":        "Xeon Gold",
		"TotalCores":   32,
		"TotalThreads": 64,
		"MaxSpeedMHz":  3600,
	})
	register("/redfish/v1/Systems/1/Memory", map[string]interface{}{
		"Members": []map[string]string{
			{"@odata.id": "/redfish/v1/Systems/1/Memory/DIMM1"},
		},
	})
	register("/redfish/v1/Systems/1/Memory/DIMM1", map[string]interface{}{
		"Name":              "DIMM A1",
		"CapacityMiB":       32768,
		"MemoryDeviceType":  "DDR5",
		"OperatingSpeedMhz": 4800,
		"Status": map[string]interface{}{
			"Health": "OK",
		},
	})
	register("/redfish/v1/Systems/1/Storage", map[string]interface{}{
		"Members": []map[string]string{
			{"@odata.id": "/redfish/v1/Systems/1/Storage/1"},
		},
	})
	register("/redfish/v1/Systems/1/Storage/1", map[string]interface{}{
		"Drives": []map[string]string{
			{"@odata.id": "/redfish/v1/Systems/1/Storage/1/Drives/1"},
		},
	})
	register("/redfish/v1/Systems/1/Storage/1/Drives/1", map[string]interface{}{
		"Name":         "Drive1",
		"Model":        "NVMe Test",
		"MediaType":    "SSD",
		"Protocol":     "NVMe",
		"CapacityGB":   960,
		"SerialNumber": "SN123",
	})
	register("/redfish/v1/Systems/1/PCIeDevices", map[string]interface{}{
		"Members": []map[string]string{
			{"@odata.id": "/redfish/v1/Systems/1/PCIeDevices/GPU1"},
		},
	})
	register("/redfish/v1/Systems/1/PCIeDevices/GPU1", map[string]interface{}{
		"Id":           "GPU1",
		"Name":         "NVIDIA H100",
		"Model":        "NVIDIA H100 PCIe",
		"Manufacturer": "NVIDIA",
		"SerialNumber": "GPU-SN-001",
		"PCIeFunctions": map[string]interface{}{
			"@odata.id": "/redfish/v1/Systems/1/PCIeDevices/GPU1/PCIeFunctions",
		},
	})
	register("/redfish/v1/Systems/1/PCIeDevices/GPU1/PCIeFunctions", map[string]interface{}{
		"Members": []map[string]string{
			{"@odata.id": "/redfish/v1/Systems/1/PCIeFunctions/GPU1F0"},
		},
	})
	register("/redfish/v1/Systems/1/PCIeFunctions/GPU1F0", map[string]interface{}{
		"FunctionId":       "0000:65:00.0",
		"VendorId":         "0x10DE",
		"DeviceId":         "0x2331",
		"ClassCode":        "0x030200",
		"CurrentLinkWidth": 16,
		"CurrentLinkSpeed": "16.0 GT/s",
		"MaxLinkWidth":     16,
		"MaxLinkSpeed":     "16.0 GT/s",
	})
	register("/redfish/v1/Chassis/1/NetworkAdapters", map[string]interface{}{
		"Members": []map[string]string{
			{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/1"},
		},
	})
	register("/redfish/v1/Chassis/1/Power", map[string]interface{}{
		"PowerSupplies": []map[string]interface{}{
			{
				"MemberId":             "PSU1",
				"Name":                 "PSU Slot 1",
				"Model":                "PWS-2K01A-1R",
				"Manufacturer":         "Delta",
				"PowerCapacityWatts":   2000,
				"PowerInputWatts":      1600,
				"LastPowerOutputWatts": 1200,
				"LineInputVoltage":     230,
				"Status": map[string]interface{}{
					"Health": "OK",
					"State":  "Enabled",
				},
			},
		},
	})
	register("/redfish/v1/Chassis/1/NetworkAdapters/1", map[string]interface{}{
		"Name":         "Mellanox",
		"Model":        "ConnectX-6",
		"SerialNumber": "NIC123",
	})
	register("/redfish/v1/Managers/1", map[string]interface{}{
		"FirmwareVersion": "1.25",
	})
	register("/redfish/v1/Managers/1/NetworkProtocol", map[string]interface{}{
		"Id": "NetworkProtocol",
	})

	ts := httptest.NewServer(mux)
	defer ts.Close()

	c := NewRedfishConnector()
	result, err := c.Collect(context.Background(), Request{
		Host:     ts.URL,
		Port:     443,
		Protocol: "redfish",
		Username: "admin",
		AuthType: "password",
		Password: "secret",
		TLSMode:  "strict",
	}, nil)
	if err != nil {
		t.Fatalf("collect failed: %v", err)
	}

	if result.Hardware == nil {
		t.Fatalf("expected hardware config")
	}
	if result.Hardware.BoardInfo.ProductName != "SYS-TEST" {
		t.Fatalf("unexpected board model: %q", result.Hardware.BoardInfo.ProductName)
	}
	if len(result.Hardware.CPUs) != 1 {
		t.Fatalf("expected one CPU, got %d", len(result.Hardware.CPUs))
	}
	if len(result.Hardware.Memory) != 1 {
		t.Fatalf("expected one DIMM, got %d", len(result.Hardware.Memory))
	}
	if len(result.Hardware.Storage) != 1 {
		t.Fatalf("expected one drive, got %d", len(result.Hardware.Storage))
	}
	if len(result.Hardware.NetworkAdapters) != 1 {
		t.Fatalf("expected one nic, got %d", len(result.Hardware.NetworkAdapters))
	}
	if len(result.Hardware.GPUs) != 1 {
		t.Fatalf("expected one gpu, got %d", len(result.Hardware.GPUs))
	}
	if result.Hardware.GPUs[0].BDF != "0000:65:00.0" {
		t.Fatalf("unexpected gpu BDF: %q", result.Hardware.GPUs[0].BDF)
	}
	if len(result.Hardware.PCIeDevices) != 1 {
		t.Fatalf("expected one pcie device, got %d", len(result.Hardware.PCIeDevices))
	}
	if len(result.Hardware.PowerSupply) != 1 {
		t.Fatalf("expected one psu, got %d", len(result.Hardware.PowerSupply))
	}
	if result.Hardware.PowerSupply[0].WattageW != 2000 {
		t.Fatalf("unexpected psu wattage: %d", result.Hardware.PowerSupply[0].WattageW)
	}
	if len(result.Hardware.Firmware) == 0 {
		t.Fatalf("expected firmware entries")
	}
	if result.RawPayloads == nil {
		t.Fatalf("expected raw payloads")
	}
	treeAny, ok := result.RawPayloads["redfish_tree"]
	if !ok {
		t.Fatalf("expected redfish_tree in raw payloads")
	}
	tree, ok := treeAny.(map[string]interface{})
	if !ok || len(tree) == 0 {
		t.Fatalf("expected non-empty redfish_tree, got %#v", treeAny)
	}
}

func TestRedfishConnectorProbe(t *testing.T) {
	mux := http.NewServeMux()
	register := func(path string, payload interface{}) {
		mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			_ = json.NewEncoder(w).Encode(payload)
		})
	}

	register("/redfish/v1", map[string]interface{}{"Name": "ServiceRoot"})
	register("/redfish/v1/Systems/1", map[string]interface{}{
		"@odata.id":  "/redfish/v1/Systems/1",
		"PowerState": "Off",
		"Actions": map[string]interface{}{
			"#ComputerSystem.Reset": map[string]interface{}{
				"target":                            "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset",
				"ResetType@Redfish.AllowableValues": []interface{}{"On", "ForceOff"},
			},
		},
	})
	ts := httptest.NewTLSServer(mux)
	defer ts.Close()

	connector := NewRedfishConnector()
	port := 443
	host := ""
	if u, err := url.Parse(ts.URL); err == nil {
		host = u.Hostname()
		if p := u.Port(); p != "" {
			fmt.Sscanf(p, "%d", &port)
		}
	}
	got, err := connector.Probe(context.Background(), Request{
		Host:     host,
		Protocol: "redfish",
		Port:     port,
		Username: "admin",
		AuthType: "password",
		Password: "secret",
		TLSMode:  "insecure",
	})
	if err != nil {
		t.Fatalf("probe failed: %v", err)
	}
	if got == nil || !got.Reachable {
		t.Fatalf("expected reachable probe result, got %+v", got)
	}
	if got.HostPoweredOn {
		t.Fatalf("expected powered off host")
	}
	if got.HostPowerState != "Off" {
		t.Fatalf("expected power state Off, got %q", got.HostPowerState)
	}
}

func TestRedfishConnectorProbe_FallsBackToPowerSummary(t *testing.T) {
	mux := http.NewServeMux()
	register := func(path string, payload interface{}) {
		mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			_ = json.NewEncoder(w).Encode(payload)
		})
	}

	register("/redfish/v1", map[string]interface{}{"Name": "ServiceRoot"})
	register("/redfish/v1/Systems", map[string]interface{}{
		"Members": []interface{}{
			map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
		},
	})
	register("/redfish/v1/Systems/1", map[string]interface{}{
		"@odata.id": "/redfish/v1/Systems/1",
		"PowerSummary": map[string]interface{}{
			"PowerState": "On",
		},
		"Actions": map[string]interface{}{
			"#ComputerSystem.Reset": map[string]interface{}{
				"target":                            "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset",
				"ResetType@Redfish.AllowableValues": []interface{}{"On", "ForceOff"},
			},
		},
	})
	ts := httptest.NewTLSServer(mux)
	defer ts.Close()

	connector := NewRedfishConnector()
	port := 443
	host := ""
	if u, err := url.Parse(ts.URL); err == nil {
		host = u.Hostname()
		if p := u.Port(); p != "" {
			fmt.Sscanf(p, "%d", &port)
		}
	}
	got, err := connector.Probe(context.Background(), Request{
		Host:     host,
		Protocol: "redfish",
		Port:     port,
		Username: "admin",
		AuthType: "password",
		Password: "secret",
		TLSMode:  "insecure",
	})
	if err != nil {
		t.Fatalf("probe failed: %v", err)
	}
	if got == nil || !got.Reachable {
		t.Fatalf("expected reachable probe result, got %+v", got)
	}
	if !got.HostPoweredOn {
		t.Fatalf("expected powered on host from PowerSummary")
	}
	if got.HostPowerState != "On" {
		t.Fatalf("expected power state On, got %q", got.HostPowerState)
	}
}

func TestParsePCIeDeviceSlot_FromNestedRedfishSlotLocation(t *testing.T) {
	doc := map[string]interface{}{
		"Id": "NIC1",
		"Slot": map[string]interface{}{
			"Lanes": 16,
			"Location": map[string]interface{}{
				"PartLocation": map[string]interface{}{
					"LocationOrdinalValue": 1,
					"LocationType":         "Slot",
					"ServiceLabel":         "PCIe Slot 1 (1)",
				},
			},
			"PCIeType": "Gen5",
			"SlotType": "FullLength",
		},
	}

	got := parsePCIeDevice(doc, nil)
	if got.Slot != "PCIe Slot 1 (1)" {
		t.Fatalf("unexpected slot: %q", got.Slot)
	}
}

func TestParsePCIeDeviceSlot_EmptyMapFallsBackToID(t *testing.T) {
	doc := map[string]interface{}{
		"Id":   "NIC42",
		"Slot": map[string]interface{}{},
	}

	got := parsePCIeDevice(doc, nil)
	if got.Slot != "NIC42" {
		t.Fatalf("unexpected slot fallback: %q", got.Slot)
	}
	if got.Slot == "map[]" {
		t.Fatalf("slot should not stringify empty map")
	}
}

func TestReplayRedfishFromRawPayloads_FallbackCollectionMembersByPrefix(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
				},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Manufacturer": "Supermicro",
				"Model":        "SYS-TEST",
				"SerialNumber": "SYS123",
			},
			// Intentionally missing /redfish/v1/Systems/1/Processors collection.
			"/redfish/v1/Systems/1/Processors/CPU1": map[string]interface{}{
				"Id":           "CPU1",
				"Model":        "Xeon Gold",
				"TotalCores":   32,
				"TotalThreads": 64,
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"},
				},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{
				"Id": "1",
			},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"},
				},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{
				"Id": "1",
			},
		},
		"redfish_fetch_errors": []map[string]interface{}{
			{"path": "/redfish/v1/Systems/1/Processors", "error": "status 500"},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}
	if got.Hardware == nil {
		t.Fatalf("expected hardware")
	}
	if len(got.Hardware.CPUs) != 1 {
		t.Fatalf("expected one CPU via prefix fallback, got %d", len(got.Hardware.CPUs))
	}
	if _, ok := got.RawPayloads["redfish_fetch_errors"]; !ok {
		t.Fatalf("expected raw payloads to preserve redfish_fetch_errors")
	}
}

func TestReplayRedfishFromRawPayloads_FallbackCollectionMembersSkipsPlaceholderNumericDocs(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
				},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Manufacturer": "Multillect",
				"Model":        "MLT-S06",
				"SerialNumber": "430044262001626",
				"Storage":      map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage"},
			},
			"/redfish/v1/Systems/1/Storage": map[string]interface{}{
				"@odata.id":           "/redfish/v1/Systems/1/Storage",
				"Members":             []interface{}{},
				"Members@odata.count": 0,
			},
			"/redfish/v1/Systems/1/Storage/1": map[string]interface{}{
				"@odata.id":          "/redfish/v1/Systems/1/Storage/1",
				"@odata.type":        "#Storage.v1_7_1.Storage",
				"Drives":             []interface{}{},
				"Drives@odata.count": "0",
				"LogicalDisk":        []interface{}{},
				"PhysicalDisk":       []interface{}{},
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"},
				},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{
				"Id":              "1",
				"NetworkAdapters": map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters"},
				"PCIeDevices":     map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices"},
			},
			"/redfish/v1/Chassis/1/NetworkAdapters": map[string]interface{}{
				"@odata.id":           "/redfish/v1/Chassis/1/NetworkAdapters",
				"Members":             []interface{}{},
				"Members@odata.count": 0,
			},
			"/redfish/v1/Chassis/1/NetworkAdapters/1": map[string]interface{}{
				"@odata.context": "/redfish/v1/$metadata#Chassis/Members/1/NetworkAdapters/Members/$entity",
				"@odata.id":      "/redfish/v1/Chassis/1/NetworkAdapters/1",
				"@odata.type":    "#NetworkAdapter.v1_0_0.Networkadapter",
				"Id":             "1",
				"Name":           "1",
			},
			"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
				"@odata.id":           "/redfish/v1/Chassis/1/PCIeDevices",
				"Members":             []interface{}{},
				"Members@odata.count": 0,
			},
			"/redfish/v1/Chassis/1/PCIeDevices/1": map[string]interface{}{
				"@odata.context": "/redfish/v1/$metadata#PCIeDevice.PCIeDevice",
				"@odata.id":      "/redfish/v1/Chassis/1/PCIeDevices/1",
				"@odata.type":    "#PCIeDevice.v1_4_0.PCIeDevice",
				"Id":             "1",
				"Name":           "PCIe Device",
			},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"},
				},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{
				"Id": "1",
			},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}
	if got.Hardware == nil {
		t.Fatalf("expected hardware")
	}
	if len(got.Hardware.NetworkAdapters) != 0 {
		t.Fatalf("expected placeholder network adapters to be skipped, got %d", len(got.Hardware.NetworkAdapters))
	}
	if len(got.Hardware.PCIeDevices) != 0 {
		t.Fatalf("expected placeholder PCIe devices to be skipped, got %d", len(got.Hardware.PCIeDevices))
	}
	if len(got.Hardware.Storage) != 0 {
		t.Fatalf("expected placeholder storage members to be skipped, got %d", len(got.Hardware.Storage))
	}
}

func TestReplayRedfishFromRawPayloads_PrefersActiveBMCInterfaceForBoardMAC(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"}},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Manufacturer": "Multillect",
				"Model":        "MLT-S06",
				"SerialNumber": "430044262001626",
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"}},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{"Id": "1"},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"}},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{
				"Id": "1",
			},
			"/redfish/v1/Managers/1/EthernetInterfaces": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1/EthernetInterfaces/eth0"},
					map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1/EthernetInterfaces/eth1"},
				},
				"Oem": map[string]interface{}{
					"Public": map[string]interface{}{
						"NcsiEnabled": true,
						"LLDP": map[string]interface{}{
							"LLDPMode": "Rx",
							"Members": []interface{}{
								map[string]interface{}{
									"EthIndex":    "eth1",
									"ChassisName": "castor.netwell.local",
									"PortDesc":    "ge-0/0/17",
									"PortId":      "531",
									"VlanId":      20,
								},
							},
						},
					},
				},
			},
			"/redfish/v1/Managers/1/EthernetInterfaces/eth0": map[string]interface{}{
				"Id":         "eth0",
				"MACAddress": "00:25:6c:70:00:13",
				"LinkStatus": "NoLink",
				"SpeedMbps":  65535,
			},
			"/redfish/v1/Managers/1/EthernetInterfaces/eth1": map[string]interface{}{
				"Id":         "eth1",
				"MACAddress": "00:25:6c:70:00:12",
				"LinkStatus": "LinkActive",
				"SpeedMbps":  1000,
				"IPv4Addresses": []interface{}{
					map[string]interface{}{
						"Address":    "172.16.41.42",
						"Gateway":    "172.16.41.1",
						"SubnetMask": "255.255.255.0",
					},
				},
			},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}
	if got.Hardware == nil {
		t.Fatalf("expected hardware")
	}
	if got.Hardware.BoardInfo.BMCMACAddress != "00:25:6C:70:00:12" {
		t.Fatalf("expected active BMC MAC from eth1, got %q", got.Hardware.BoardInfo.BMCMACAddress)
	}
	summary, ok := got.RawPayloads["redfish_bmc_network_summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected redfish_bmc_network_summary")
	}
	if summary["interface_id"] != "eth1" {
		t.Fatalf("expected eth1 summary, got %#v", summary["interface_id"])
	}
}

func TestReplayRedfishFromRawPayloads_AddsSensorsListHintSummary(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"}},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Manufacturer": "Multillect",
				"Model":        "MLT-S06",
				"SerialNumber": "430044262001626",
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"}},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{"Id": "1"},
			"/redfish/v1/Chassis/1/SensorsList": map[string]interface{}{
				"SensorsList": []interface{}{
					map[string]interface{}{"SensorName": "DIMM000_Status", "SensorType": "Memory", "Status": "OK"},
					map[string]interface{}{"SensorName": "DIMM001_Status", "SensorType": "Memory", "Status": "nop"},
					map[string]interface{}{"SensorName": "DIMM100_Status", "SensorType": "Memory", "Status": "OK"},
					map[string]interface{}{"SensorName": "HDD0_F_Status", "SensorType": "Drive Slot", "Status": "nop"},
					map[string]interface{}{"SensorName": "NVME0_F_Status", "SensorType": "Drive Slot", "Status": "nop"},
					map[string]interface{}{"SensorName": "Logical_Drive", "SensorType": "Drive Slot", "Status": "OK"},
				},
			},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"}},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{"Id": "1"},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}
	hints, ok := got.RawPayloads["redfish_sensor_hints"].(map[string]any)
	if !ok {
		t.Fatalf("expected redfish_sensor_hints")
	}
	memHints, ok := hints["memory_slots"].(map[string]any)
	if !ok {
		t.Fatalf("expected memory_slots hint")
	}
	if asInt(memHints["present_count"]) != 2 {
		t.Fatalf("expected 2 present memory slot hints, got %#v", memHints["present_count"])
	}
	driveHints, ok := hints["drive_slots"].(map[string]any)
	if !ok {
		t.Fatalf("expected drive_slots hint")
	}
	if asInt(driveHints["physical_total"]) != 2 {
		t.Fatalf("expected 2 physical drive slots, got %#v", driveHints["physical_total"])
	}
	if driveHints["logical_drive_status"] != "OK" {
		t.Fatalf("expected logical drive status OK, got %#v", driveHints["logical_drive_status"])
	}
	foundMemoryEvent := false
	foundDriveEvent := false
	for _, ev := range got.Events {
		if strings.Contains(ev.Description, "Memory slot sensors report 2 populated positions out of 3") {
			foundMemoryEvent = true
		}
		if strings.Contains(ev.Description, "Drive slot sensors report 0 active physical slots out of 2") {
			foundDriveEvent = true
		}
	}
	if !foundMemoryEvent {
		t.Fatalf("expected memory slot hint event")
	}
	if !foundDriveEvent {
		t.Fatalf("expected drive slot hint event")
	}
}

func TestReplayRedfishFromRawPayloads_PreservesSourceTimezoneAndUTCCollectedAt(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"}},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Manufacturer": "Inspur",
				"Model":        "NF5688M7",
				"SerialNumber": "23E100051",
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"}},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{"Id": "1"},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"}},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{
				"DateTime":            "2026-02-28T04:18:18+08:00",
				"DateTimeLocalOffset": "+08:00",
			},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}
	if got.SourceTimezone != "+08:00" {
		t.Fatalf("expected source_timezone +08:00, got %q", got.SourceTimezone)
	}
	wantCollectedAt := time.Date(2026, 2, 27, 20, 18, 18, 0, time.UTC)
	if !got.CollectedAt.Equal(wantCollectedAt) {
		t.Fatalf("expected collected_at %s, got %s", wantCollectedAt, got.CollectedAt)
	}
	if got.RawPayloads["source_timezone"] != "+08:00" {
		t.Fatalf("expected source_timezone in raw payloads, got %#v", got.RawPayloads["source_timezone"])
	}
}

func TestReplayRedfishFromRawPayloads_ParsesInlineThresholdAndDiscreteSensors(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
				},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Id":           "1",
				"Manufacturer": "Inspur",
				"Model":        "NF5688M7",
				"SerialNumber": "23E100051",
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"},
				},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{
				"Id": "1",
			},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"},
				},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{
				"Id": "1",
			},
			"/redfish/v1/Chassis/1/ThresholdSensors": map[string]interface{}{
				"Sensors": []interface{}{
					map[string]interface{}{
						"Name":         "Inlet_Temp",
						"Reading":      16,
						"ReadingUnits": "deg_c",
						"State":        "Enabled",
					},
				},
			},
			"/redfish/v1/Chassis/1/DiscreteSensors": map[string]interface{}{
				"Sensors": []interface{}{
					map[string]interface{}{
						"Name":  "PSU_Redundant",
						"State": "Disabled",
					},
				},
			},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}
	if len(got.Sensors) == 0 {
		t.Fatalf("expected sensors from inline ThresholdSensors")
	}
	foundSensor := false
	for _, s := range got.Sensors {
		if s.Name == "Inlet_Temp" {
			foundSensor = true
			break
		}
	}
	if !foundSensor {
		t.Fatalf("expected Inlet_Temp sensor in replay output")
	}
	foundEvent := false
	for _, ev := range got.Events {
		if ev.EventType == "Discrete Sensor Status" && ev.SensorName == "PSU_Redundant" {
			foundEvent = true
			break
		}
	}
	if !foundEvent {
		t.Fatalf("expected discrete sensor warning event from inline DiscreteSensors")
	}
}

func TestReplayRedfishFromRawPayloads_CollectsThermalAndPowerSensors(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
				},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Id":           "1",
				"Manufacturer": "Inspur",
				"Model":        "NF5688M7",
				"SerialNumber": "23E100051",
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"},
				},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{
				"Id": "1",
			},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"},
				},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{
				"Id": "1",
			},
			"/redfish/v1/Chassis/1/Thermal": map[string]interface{}{
				"Fans": []interface{}{
					map[string]interface{}{
						"Name":         "FAN0_F_Speed",
						"Reading":      9279,
						"ReadingUnits": "RPM",
						"Status": map[string]interface{}{
							"Health": "OK",
							"State":  "Enabled",
						},
					},
				},
				"Temperatures": []interface{}{
					map[string]interface{}{
						"Name":           "CPU0_Temp",
						"ReadingCelsius": 44,
						"Status": map[string]interface{}{
							"Health": "OK",
							"State":  "Enabled",
						},
					},
				},
			},
			"/redfish/v1/Chassis/1/Power": map[string]interface{}{
				"Oem": map[string]interface{}{
					"Public": map[string]interface{}{
						"TotalPower":              1836,
						"CurrentCPUPowerWatts":    304,
						"CurrentMemoryPowerWatts": 75,
						"CurrentFANPowerWatts":    180,
					},
				},
				"PowerControl": []interface{}{
					map[string]interface{}{
						"Name":               "System Power Control 1",
						"PowerConsumedWatts": 1836,
						"Status": map[string]interface{}{
							"Health": "OK",
							"State":  "Enabled",
						},
					},
				},
				"PowerSupplies": []interface{}{
					map[string]interface{}{
						"Name":                 "Power Supply 1",
						"PowerInputWatts":      180,
						"LastPowerOutputWatts": 155,
						"LineInputVoltage":     223.25,
						"Status": map[string]interface{}{
							"Health": "OK",
							"State":  "Enabled",
						},
					},
				},
			},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}
	if len(got.Sensors) == 0 {
		t.Fatalf("expected non-empty sensors")
	}
	expected := map[string]bool{
		"FAN0_F_Speed":                    false,
		"CPU0_Temp":                       false,
		"Total_Power":                     false,
		"System Power Control 1_Consumed": false,
		"Power Supply 1_InputPower":       false,
	}
	for _, s := range got.Sensors {
		if _, ok := expected[s.Name]; ok {
			expected[s.Name] = true
		}
	}
	for name, found := range expected {
		if !found {
			t.Fatalf("expected sensor %q in replay output", name)
		}
	}
}

func TestEnrichNICFromPCIeFunctions(t *testing.T) {
	nic := parseNIC(map[string]interface{}{
		"Id":           "1",
		"Model":        "MCX75310AAS-NEAT",
		"Manufacturer": "Supermicro",
		"SerialNumber": "NIC-SN-1",
		"Controllers": []interface{}{
			map[string]interface{}{
				"Links": map[string]interface{}{
					"PCIeDevices": []interface{}{
						map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/NIC1"},
					},
				},
				"Location": map[string]interface{}{
					"PartLocation": map[string]interface{}{"ServiceLabel": "PCIe Slot 1 (1)"},
				},
			},
		},
	})

	pcieDoc := map[string]interface{}{
		"Id": "NIC1",
		"PCIeFunctions": map[string]interface{}{
			"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/NIC1/PCIeFunctions",
		},
	}
	functionDocs := []map[string]interface{}{
		{
			"FunctionId":          "0000:17:00.0",
			"VendorId":            "0x15b3",
			"DeviceId":            "0x1021",
			"SerialNumber":        "MT-SN-0001",
			"PartNumber":          "MCX623106AC-CDAT",
			"CurrentLinkWidth":    16,
			"CurrentLinkSpeedGTs": "32 GT/s",
			"MaxLinkWidth":        16,
			"MaxLinkSpeedGTs":     "32 GT/s",
		},
	}

	enrichNICFromPCIe(&nic, pcieDoc, functionDocs, nil)
	if nic.VendorID != 0x15b3 || nic.DeviceID != 0x1021 {
		t.Fatalf("unexpected NIC IDs: vendor=%#x device=%#x", nic.VendorID, nic.DeviceID)
	}
	if nic.Location != "PCIe Slot 1 (1)" {
		t.Fatalf("unexpected NIC location: %q", nic.Location)
	}
	if nic.BDF != "0000:17:00.0" {
		t.Fatalf("unexpected NIC BDF: %q", nic.BDF)
	}
	if nic.SerialNumber != "NIC-SN-1" {
		t.Fatalf("expected existing NIC serial to be preserved, got %q", nic.SerialNumber)
	}
	if nic.PartNumber != "MCX623106AC-CDAT" {
		t.Fatalf("expected NIC part number from PCIe function, got %q", nic.PartNumber)
	}
	if nic.LinkWidth != 16 || nic.MaxLinkWidth != 16 {
		t.Fatalf("unexpected NIC link width state: current=%d max=%d", nic.LinkWidth, nic.MaxLinkWidth)
	}
	if nic.LinkSpeed != "32 GT/s" || nic.MaxLinkSpeed != "32 GT/s" {
		t.Fatalf("unexpected NIC link speed state: current=%q max=%q", nic.LinkSpeed, nic.MaxLinkSpeed)
	}
}

func TestEnrichNICFromPCIeFunctions_FillsMissingIdentityFromFunctionDoc(t *testing.T) {
	nic := parseNIC(map[string]interface{}{
		"Id": "DevType7_NIC1",
		"Controllers": []interface{}{
			map[string]interface{}{
				"ControllerCapabilities": map[string]interface{}{
					"NetworkPortCount": 1,
				},
			},
			map[string]interface{}{
				"ControllerCapabilities": map[string]interface{}{
					"NetworkPortCount": 1,
				},
			},
		},
	})

	pcieDoc := map[string]interface{}{
		"Slot": map[string]interface{}{
			"Location": map[string]interface{}{
				"PartLocation": map[string]interface{}{
					"ServiceLabel": "RISER4",
				},
			},
		},
	}
	functionDocs := []map[string]interface{}{
		{
			"FunctionId":   "0000:0f:00.0",
			"VendorId":     "0x15b3",
			"DeviceId":     "0x101f",
			"SerialNumber": "MT2412X00001",
			"PartNumber":   "MCX623432AC-GDA_Ax",
		},
	}

	enrichNICFromPCIe(&nic, pcieDoc, functionDocs, nil)
	if nic.Slot != "RISER4" {
		t.Fatalf("expected slot from PCIe slot label, got %q", nic.Slot)
	}
	if nic.Location != "RISER4" {
		t.Fatalf("expected location from PCIe slot label, got %q", nic.Location)
	}
	if nic.PortCount != 2 {
		t.Fatalf("expected combined port count from controllers, got %d", nic.PortCount)
	}
	if nic.SerialNumber != "MT2412X00001" {
		t.Fatalf("expected serial from PCIe function, got %q", nic.SerialNumber)
	}
	if nic.PartNumber != "MCX623432AC-GDA_Ax" {
		t.Fatalf("expected part number from PCIe function, got %q", nic.PartNumber)
	}
	if nic.BDF != "0000:0f:00.0" {
		t.Fatalf("expected BDF from PCIe function, got %q", nic.BDF)
	}
}

func TestReplayCollectNICs_UsesNetworkDeviceFunctionPCIeFunctionLink(t *testing.T) {
	tree := map[string]interface{}{
		"/redfish/v1/Chassis/1/NetworkAdapters": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1"},
			},
		},
		"/redfish/v1/Chassis/1/NetworkAdapters/NIC1": map[string]interface{}{
			"Id":   "DevType7_NIC1",
			"Name": "NetworkAdapter_1",
			"Controllers": []interface{}{
				map[string]interface{}{
					"ControllerCapabilities": map[string]interface{}{
						"NetworkPortCount": 2,
					},
					"Links": map[string]interface{}{
						"PCIeDevices": []interface{}{
							map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/00_0F_00"},
						},
					},
				},
			},
			"NetworkDeviceFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions",
			},
		},
		"/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions/Function0"},
			},
		},
		"/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions/Function0": map[string]interface{}{
			"Id": "Function0",
			"Links": map[string]interface{}{
				"PCIeFunction": map[string]interface{}{
					"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions/Function0",
				},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/00_0F_00": map[string]interface{}{
			"Id":              "00_0F_00",
			"Name":            "PCIeDevice_00_0F_00",
			"Manufacturer":    "Mellanox Technologies",
			"FirmwareVersion": "26.43.25.66",
			"Slot": map[string]interface{}{
				"Location": map[string]interface{}{
					"PartLocation": map[string]interface{}{
						"ServiceLabel": "RISER4",
					},
				},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions/Function0": map[string]interface{}{
			"Id":           "Function0",
			"FunctionId":   "0000:0f:00.0",
			"VendorId":     "0x15b3",
			"DeviceId":     "0x101f",
			"SerialNumber": "MT2412X00001",
			"PartNumber":   "MCX623432AC-GDA_Ax",
		},
	}

	r := redfishSnapshotReader{tree: tree}
	nics := r.collectNICs([]string{"/redfish/v1/Chassis/1"})
	if len(nics) != 1 {
		t.Fatalf("expected one NIC, got %d", len(nics))
	}
	if nics[0].Slot != "RISER4" {
		t.Fatalf("expected slot from PCIe device, got %q", nics[0].Slot)
	}
	if nics[0].SerialNumber != "MT2412X00001" {
		t.Fatalf("expected serial from NetworkDeviceFunction PCIeFunction link, got %q", nics[0].SerialNumber)
	}
	if nics[0].PartNumber != "MCX623432AC-GDA_Ax" {
		t.Fatalf("expected part number from linked PCIeFunction, got %q", nics[0].PartNumber)
	}
	if nics[0].BDF != "0000:0f:00.0" {
		t.Fatalf("expected BDF from linked PCIeFunction, got %q", nics[0].BDF)
	}
	if nics[0].Model != "MT2894 Family [ConnectX-6 Lx]" {
		t.Fatalf("expected model resolved from PCI IDs, got %q", nics[0].Model)
	}
}

func TestReplayEnrichNICsFromNetworkInterfaces_DoesNotCreateGhostForLinkedAdapter(t *testing.T) {
	tree := map[string]interface{}{
		"/redfish/v1/Chassis/1/NetworkAdapters": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1"},
			},
		},
		"/redfish/v1/Chassis/1/NetworkAdapters/NIC1": map[string]interface{}{
			"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1",
			"Id":        "DevType7_NIC1",
			"Name":      "NetworkAdapter_1",
			"Controllers": []interface{}{
				map[string]interface{}{
					"ControllerCapabilities": map[string]interface{}{
						"NetworkPortCount": 1,
					},
					"Links": map[string]interface{}{
						"PCIeDevices": []interface{}{
							map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/00_0F_00"},
						},
					},
				},
				map[string]interface{}{
					"ControllerCapabilities": map[string]interface{}{
						"NetworkPortCount": 1,
					},
					"Links": map[string]interface{}{
						"PCIeDevices": []interface{}{
							map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/00_0F_00"},
						},
					},
				},
			},
			"NetworkDeviceFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions",
			},
		},
		"/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions/Function0"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions/Function1"},
			},
		},
		"/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions/Function0": map[string]interface{}{
			"Id": "Function0",
			"Ethernet": map[string]interface{}{
				"MACAddress":          "CC:40:F3:D6:9E:DE",
				"PermanentMACAddress": "CC:40:F3:D6:9E:DE",
			},
			"Links": map[string]interface{}{
				"PCIeFunction": map[string]interface{}{
					"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions/Function0",
				},
			},
		},
		"/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions/Function1": map[string]interface{}{
			"Id": "Function1",
			"Ethernet": map[string]interface{}{
				"MACAddress":          "CC:40:F3:D6:9E:DF",
				"PermanentMACAddress": "CC:40:F3:D6:9E:DF",
			},
			"Links": map[string]interface{}{
				"PCIeFunction": map[string]interface{}{
					"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions/Function1",
				},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/00_0F_00": map[string]interface{}{
			"Id":              "00_0F_00",
			"Name":            "PCIeDevice_00_0F_00",
			"Manufacturer":    "Mellanox Technologies",
			"FirmwareVersion": "26.43.25.66",
			"Slot": map[string]interface{}{
				"Location": map[string]interface{}{
					"PartLocation": map[string]interface{}{
						"ServiceLabel": "RISER4",
					},
				},
			},
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions/Function0"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions/Function1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions/Function0": map[string]interface{}{
			"FunctionId":   "0000:0f:00.0",
			"VendorId":     "0x15b3",
			"DeviceId":     "0x101f",
			"DeviceClass":  "NetworkController",
			"SerialNumber": "N/A",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/00_0F_00/PCIeFunctions/Function1": map[string]interface{}{
			"FunctionId":  "0000:0f:00.1",
			"VendorId":    "0x15b3",
			"DeviceId":    "0x101f",
			"DeviceClass": "NetworkController",
		},
		"/redfish/v1/Systems/1/NetworkInterfaces": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/NetworkInterfaces/DevType7_NIC1"},
			},
		},
		"/redfish/v1/Systems/1/NetworkInterfaces/DevType7_NIC1": map[string]interface{}{
			"Id":   "DevType7_NIC1",
			"Name": "NetworkAdapter_1",
			"Links": map[string]interface{}{
				"NetworkAdapter": map[string]interface{}{
					"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1",
				},
			},
			"Status": map[string]interface{}{
				"Health": "OK",
				"State":  "Disabled",
			},
		},
	}

	r := redfishSnapshotReader{tree: tree}
	nics := r.collectNICs([]string{"/redfish/v1/Chassis/1"})
	r.enrichNICsFromNetworkInterfaces(&nics, []string{"/redfish/v1/Systems/1"})
	if len(nics) != 1 {
		t.Fatalf("expected linked network interface to reuse existing NIC, got %d: %+v", len(nics), nics)
	}
	if nics[0].Slot != "RISER4" {
		t.Fatalf("expected enriched slot to stay canonical, got %q", nics[0].Slot)
	}
	if nics[0].Model != "MT2894 Family [ConnectX-6 Lx]" {
		t.Fatalf("expected resolved Mellanox model, got %q", nics[0].Model)
	}
	if len(nics[0].MACAddresses) != 2 {
		t.Fatalf("expected both MACs to stay on one NIC, got %+v", nics[0].MACAddresses)
	}
}

func TestParseNIC_PortCountFromControllerCapabilities(t *testing.T) {
	nic := parseNIC(map[string]interface{}{
		"Id": "1",
		"Controllers": []interface{}{
			map[string]interface{}{
				"ControllerCapabilities": map[string]interface{}{
					"NetworkPortCount": 2,
				},
			},
		},
	})

	if nic.PortCount != 2 {
		t.Fatalf("expected port_count=2, got %d", nic.PortCount)
	}
}

func TestParseNIC_PrefersControllerSlotLabelAndPCIeInterface(t *testing.T) {
	nic := parseNIC(map[string]interface{}{
		"Id":           "1",
		"Model":        "MCX75310AAS-NEAT",
		"Manufacturer": "Supermicro",
		"Controllers": []interface{}{
			map[string]interface{}{
				"Location": map[string]interface{}{
					"PartLocation": map[string]interface{}{
						"ServiceLabel": "PCIe Slot 1 (1)",
					},
				},
				"PCIeInterface": map[string]interface{}{
					"LanesInUse":  16,
					"MaxLanes":    16,
					"PCIeType":    "Gen5",
					"MaxPCIeType": "Gen5",
				},
			},
		},
	})

	if nic.Slot != "PCIe Slot 1 (1)" {
		t.Fatalf("expected slot from controller location, got %q", nic.Slot)
	}
	if nic.Location != "PCIe Slot 1 (1)" {
		t.Fatalf("expected location from controller location, got %q", nic.Location)
	}
	if nic.LinkWidth != 16 || nic.MaxLinkWidth != 16 {
		t.Fatalf("expected link widths from controller PCIeInterface, got current=%d max=%d", nic.LinkWidth, nic.MaxLinkWidth)
	}
	if nic.LinkSpeed != "Gen5" || nic.MaxLinkSpeed != "Gen5" {
		t.Fatalf("expected link speeds from controller PCIeInterface, got current=%q max=%q", nic.LinkSpeed, nic.MaxLinkSpeed)
	}
}

func TestParseNIC_xFusionMaxlanesAndOEMLinkWidth(t *testing.T) {
	// xFusion uses "Maxlanes" (lowercase 'l') in PCIeInterface, not "MaxLanes".
	// xFusion also stores per-function link width as Oem.xFusion.LinkWidth = "X8".
	nic := parseNIC(map[string]interface{}{
		"Id":    "OCPCard1",
		"Model": "ConnectX-6 Lx",
		"Controllers": []interface{}{
			map[string]interface{}{
				"PCIeInterface": map[string]interface{}{
					"LanesInUse":  8,
					"Maxlanes":    8, // xFusion uses lowercase 'l'
					"PCIeType":    "Gen4",
					"MaxPCIeType": "Gen4",
				},
			},
		},
	})
	if nic.LinkWidth != 8 || nic.MaxLinkWidth != 8 {
		t.Fatalf("expected link widths 8/8 from xFusion Maxlanes, got current=%d max=%d", nic.LinkWidth, nic.MaxLinkWidth)
	}

	// enrichNICFromPCIe: OEM xFusion LinkWidth on a PCIeFunction doc.
	nic2 := models.NetworkAdapter{}
	fnDoc := map[string]interface{}{
		"Oem": map[string]interface{}{
			"xFusion": map[string]interface{}{
				"LinkWidth":        "X8",
				"LinkWidthAbility": "X8",
				"LinkSpeed":        "Gen4 (16.0GT/s)",
				"LinkSpeedAbility": "Gen4 (16.0GT/s)",
			},
		},
	}
	enrichNICFromPCIe(&nic2, map[string]interface{}{}, []map[string]interface{}{fnDoc}, nil)
	if nic2.LinkWidth != 8 || nic2.MaxLinkWidth != 8 {
		t.Fatalf("expected link width 8 from xFusion OEM LinkWidth, got current=%d max=%d", nic2.LinkWidth, nic2.MaxLinkWidth)
	}
	if nic2.LinkSpeed != "Gen4 (16.0GT/s)" || nic2.MaxLinkSpeed != "Gen4 (16.0GT/s)" {
		t.Fatalf("expected link speed from xFusion OEM LinkSpeed, got current=%q max=%q", nic2.LinkSpeed, nic2.MaxLinkSpeed)
	}
}

func TestParseNIC_DropsUnrealisticPortCount(t *testing.T) {
	nic := parseNIC(map[string]interface{}{
		"Id": "1",
		"Controllers": []interface{}{
			map[string]interface{}{
				"ControllerCapabilities": map[string]interface{}{
					"NetworkPortCount": 825307750,
				},
			},
		},
	})

	if nic.PortCount != 0 {
		t.Fatalf("expected unrealistic port count to be dropped, got %d", nic.PortCount)
	}
}

func TestParsePCIeDevice_PrefersFunctionClassOverDeviceType(t *testing.T) {
	doc := map[string]interface{}{
		"Id":         "NIC1",
		"DeviceType": "SingleFunction",
		"Model":      "MCX75310AAS-NEAT",
		"PartNumber": "MCX75310AAS-NEAT",
	}
	functionDocs := []map[string]interface{}{
		{
			"DeviceClass": "NetworkController",
			"VendorId":    "0x15b3",
			"DeviceId":    "0x1021",
		},
	}

	got := parsePCIeDevice(doc, functionDocs)
	if got.DeviceClass == "SingleFunction" {
		t.Fatalf("device class should not keep generic redfish DeviceType")
	}
	if got.DeviceClass == "" {
		t.Fatalf("device class should be resolved")
	}
}

func TestParsePCIeDevice_DoesNotPromotePartNumberToDeviceClass(t *testing.T) {
	doc := map[string]interface{}{
		"Id":         "NIC1",
		"DeviceType": "SingleFunction",
		"Model":      "MCX75310AAS-NEAT",
		"PartNumber": "MCX75310AAS-NEAT",
	}

	got := parsePCIeDevice(doc, nil)
	if got.DeviceClass != "PCIe device" {
		t.Fatalf("expected generic PCIe class fallback, got %q", got.DeviceClass)
	}
	if got.PartNumber != "MCX75310AAS-NEAT" {
		t.Fatalf("expected part number to stay intact, got %q", got.PartNumber)
	}
}

func TestParsePCIeComponents_DoNotTreatNumericFunctionIDAsBDF(t *testing.T) {
	pcieFn := parsePCIeFunction(map[string]interface{}{
		"Id":          "1",
		"FunctionId":  "1",
		"DeviceClass": "NetworkController",
		"VendorId":    "0x15b3",
		"DeviceId":    "0x1021",
	}, 1)
	if pcieFn.BDF != "" {
		t.Fatalf("expected empty BDF for numeric FunctionId, got %q", pcieFn.BDF)
	}

	gpu := parseGPU(map[string]interface{}{
		"Id":   "GPU1",
		"Name": "GPU1",
		"PCIeFunctions": map[string]interface{}{
			"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions",
		},
	}, []map[string]interface{}{
		{
			"FunctionId": "1",
			"VendorId":   "0x10de",
			"DeviceId":   "0x2331",
		},
	}, 1)
	if gpu.BDF != "" {
		t.Fatalf("expected GPU BDF to stay empty when only numeric FunctionId exists, got %q", gpu.BDF)
	}

	nic := parseNIC(map[string]interface{}{"Id": "1"})
	enrichNICFromPCIe(&nic, map[string]interface{}{}, []map[string]interface{}{
		{
			"FunctionId": "1",
			"VendorId":   "0x15b3",
			"DeviceId":   "0x1021",
		},
	}, nil)
	if nic.BDF != "" {
		t.Fatalf("expected NIC BDF to stay empty when only numeric FunctionId exists, got %q", nic.BDF)
	}
}

func TestParseComponents_UseNestedSerialNumberFallback(t *testing.T) {
	doc := map[string]interface{}{
		"Name":         "dev0",
		"Id":           "dev0",
		"Model":        "model0",
		"Manufacturer": "vendor0",
		"SerialNumber": "N/A",
		"Oem": map[string]interface{}{
			"VendorX": map[string]interface{}{
				"SerialNumber": "SN-OK-001",
			},
		},
	}

	cpus := parseCPUs([]map[string]interface{}{doc})
	if len(cpus) != 1 || cpus[0].SerialNumber != "SN-OK-001" {
		t.Fatalf("expected CPU serial fallback, got %+v", cpus)
	}

	dimms := parseMemory([]map[string]interface{}{doc})
	if len(dimms) != 1 || dimms[0].SerialNumber != "SN-OK-001" {
		t.Fatalf("expected DIMM serial fallback, got %+v", dimms)
	}

	drive := parseDrive(doc)
	if drive.SerialNumber != "SN-OK-001" {
		t.Fatalf("expected drive serial fallback, got %q", drive.SerialNumber)
	}

	nic := parseNIC(doc)
	if nic.SerialNumber != "SN-OK-001" {
		t.Fatalf("expected NIC serial fallback, got %q", nic.SerialNumber)
	}

	psu := parsePSU(doc, 1)
	if psu.SerialNumber != "SN-OK-001" {
		t.Fatalf("expected PSU serial fallback, got %q", psu.SerialNumber)
	}

	pcie := parsePCIeDevice(doc, nil)
	if pcie.SerialNumber != "SN-OK-001" {
		t.Fatalf("expected PCIe device serial fallback, got %q", pcie.SerialNumber)
	}

	pcieFn := parsePCIeFunction(doc, 1)
	if pcieFn.SerialNumber != "SN-OK-001" {
		t.Fatalf("expected PCIe function serial fallback, got %q", pcieFn.SerialNumber)
	}
}

func TestParseCPU_UsesPublicSerialAsPPINAndCurrentSpeedMHz(t *testing.T) {
	cpus := parseCPUs([]map[string]interface{}{
		{
			"Id":                "CPU0",
			"Model":             "Intel Xeon",
			"TotalCores":        48,
			"TotalThreads":      96,
			"MaxSpeedMHz":       4000,
			"OperatingSpeedMHz": 0,
			"Oem": map[string]interface{}{
				"Public": map[string]interface{}{
					"SerialNumber":    "6FB5241E81CECDFD",
					"CurrentSpeedMHz": 2700,
				},
			},
		},
	})

	if len(cpus) != 1 {
		t.Fatalf("expected one CPU, got %d", len(cpus))
	}
	if cpus[0].PPIN != "6FB5241E81CECDFD" {
		t.Fatalf("expected PPIN from Oem.Public.SerialNumber, got %+v", cpus[0])
	}
	if cpus[0].SerialNumber != "" {
		t.Fatalf("expected empty CPU serial number when only Public serial exists, got %+v", cpus[0])
	}
	if cpus[0].FrequencyMHz != 2700 {
		t.Fatalf("expected CPU frequency from Oem.Public.CurrentSpeedMHz, got %+v", cpus[0])
	}
}

func TestParseCPUAndMemory_CollectOemDetails(t *testing.T) {
	cpus := parseCPUs([]map[string]interface{}{
		{
			"Id":                 "CPU0",
			"Model":              "Intel Xeon",
			"CorrectableErrors":  7,
			"TemperatureCelsius": 63,
			"Oem": map[string]interface{}{
				"VendorX": map[string]interface{}{
					"MicrocodeVersion":    "0x2b000643",
					"UncorrectableErrors": 1,
					"ThermalThrottled":    true,
				},
			},
		},
	})
	if len(cpus) != 1 || cpus[0].Details == nil {
		t.Fatalf("expected CPU details, got %+v", cpus)
	}
	if cpus[0].Details["microcode"] != "0x2b000643" {
		t.Fatalf("expected CPU microcode detail, got %#v", cpus[0].Details)
	}
	if cpus[0].Details["correctable_error_count"] != int64(7) || cpus[0].Details["uncorrectable_error_count"] != int64(1) {
		t.Fatalf("expected CPU error counters, got %#v", cpus[0].Details)
	}
	if cpus[0].Details["throttled"] != true || cpus[0].Details["temperature_c"] != 63.0 {
		t.Fatalf("expected CPU thermal details, got %#v", cpus[0].Details)
	}

	dimms := parseMemory([]map[string]interface{}{
		{
			"Id":            "DIMM0",
			"DeviceLocator": "CPU0_C0D0",
			"CapacityMiB":   32768,
			"SerialNumber":  "DIMM-001",
			"Oem": map[string]interface{}{
				"VendorX": map[string]interface{}{
					"CorrectableECCErrorCount":    12,
					"UncorrectableECCErrorCount":  2,
					"TemperatureC":                41.5,
					"SpareBlocksRemainingPercent": 88,
					"PerformanceDegraded":         true,
					"DataLossDetected":            false,
				},
			},
		},
	})
	if len(dimms) != 1 || dimms[0].Details == nil {
		t.Fatalf("expected DIMM details, got %+v", dimms)
	}
	if dimms[0].Details["correctable_ecc_error_count"] != int64(12) || dimms[0].Details["uncorrectable_ecc_error_count"] != int64(2) {
		t.Fatalf("expected DIMM ECC counters, got %#v", dimms[0].Details)
	}
	if dimms[0].Details["temperature_c"] != 41.5 || dimms[0].Details["spare_blocks_remaining_pct"] != 88.0 {
		t.Fatalf("expected DIMM telemetry details, got %#v", dimms[0].Details)
	}
	if dimms[0].Details["performance_degraded"] != true || dimms[0].Details["data_loss_detected"] != false {
		t.Fatalf("expected DIMM boolean health details, got %#v", dimms[0].Details)
	}
}

func TestReplayRedfishFromRawPayloads_UsesProcessorAndMemoryMetrics(t *testing.T) {
	rawPayloads := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
				},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Id": "1",
				"Processors": map[string]interface{}{
					"@odata.id": "/redfish/v1/Systems/1/Processors",
				},
				"Memory": map[string]interface{}{
					"@odata.id": "/redfish/v1/Systems/1/Memory",
				},
			},
			"/redfish/v1/Systems/1/Processors": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Processors/CPU0"},
				},
			},
			"/redfish/v1/Systems/1/Processors/CPU0": map[string]interface{}{
				"Id":            "CPU0",
				"ProcessorType": "CPU",
				"Model":         "Intel Xeon",
				"ProcessorMetrics": map[string]interface{}{
					"@odata.id": "/redfish/v1/Systems/1/Processors/CPU0/ProcessorMetrics",
				},
			},
			"/redfish/v1/Systems/1/Processors/CPU0/ProcessorMetrics": map[string]interface{}{
				"CorrectableErrors":  10,
				"ThermalThrottled":   true,
				"MicrocodeVersion":   "0x2b000643",
				"TemperatureCelsius": 66,
			},
			"/redfish/v1/Systems/1/Memory": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Memory/DIMM0"},
				},
			},
			"/redfish/v1/Systems/1/Memory/DIMM0": map[string]interface{}{
				"Id":            "DIMM0",
				"DeviceLocator": "CPU0_C0D0",
				"CapacityMiB":   32768,
				"SerialNumber":  "DIMM-001",
				"MemoryMetrics": map[string]interface{}{
					"@odata.id": "/redfish/v1/Systems/1/Memory/DIMM0/MemoryMetrics",
				},
			},
			"/redfish/v1/Systems/1/Memory/DIMM0/MemoryMetrics": map[string]interface{}{
				"CorrectableECCErrorCount":    14,
				"TemperatureCelsius":          42,
				"PerformanceDegraded":         true,
				"SpareBlocksRemainingPercent": 91,
			},
		},
	}

	result, err := ReplayRedfishFromRawPayloads(rawPayloads, nil)
	if err != nil {
		t.Fatalf("ReplayRedfishFromRawPayloads() failed: %v", err)
	}
	if len(result.Hardware.CPUs) != 1 || result.Hardware.CPUs[0].Details == nil {
		t.Fatalf("expected CPU details from replay metrics, got %+v", result.Hardware.CPUs)
	}
	if result.Hardware.CPUs[0].Details["correctable_error_count"] != int64(10) || result.Hardware.CPUs[0].Details["microcode"] != "0x2b000643" {
		t.Fatalf("expected CPU replay metrics details, got %#v", result.Hardware.CPUs[0].Details)
	}
	if len(result.Hardware.Memory) != 1 || result.Hardware.Memory[0].Details == nil {
		t.Fatalf("expected memory details from replay metrics, got %+v", result.Hardware.Memory)
	}
	if result.Hardware.Memory[0].Details["correctable_ecc_error_count"] != int64(14) || result.Hardware.Memory[0].Details["performance_degraded"] != true {
		t.Fatalf("expected DIMM replay metrics details, got %#v", result.Hardware.Memory[0].Details)
	}
}

func TestReplayRedfishFromRawPayloads_UsesDriveMetrics(t *testing.T) {
	rawPayloads := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
				},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Id": "1",
				"Storage": map[string]interface{}{
					"@odata.id": "/redfish/v1/Systems/1/Storage",
				},
			},
			"/redfish/v1/Systems/1/Storage": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/RAID1"},
				},
			},
			"/redfish/v1/Systems/1/Storage/RAID1": map[string]interface{}{
				"Id": "RAID1",
				"Drives": map[string]interface{}{
					"@odata.id": "/redfish/v1/Systems/1/Storage/RAID1/Drives",
				},
			},
			"/redfish/v1/Systems/1/Storage/RAID1/Drives": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/RAID1/Drives/Drive0"},
				},
			},
			"/redfish/v1/Systems/1/Storage/RAID1/Drives/Drive0": map[string]interface{}{
				"Id":           "Drive0",
				"Model":        "NVMe SSD",
				"SerialNumber": "SSD-001",
				"DriveMetrics": map[string]interface{}{
					"@odata.id": "/redfish/v1/Systems/1/Storage/RAID1/Drives/Drive0/DriveMetrics",
				},
			},
			"/redfish/v1/Systems/1/Storage/RAID1/Drives/Drive0/DriveMetrics": map[string]interface{}{
				"PowerOnHours":          1001,
				"MediaErrors":           3,
				"AvailableSparePercent": 92,
				"TemperatureCelsius":    37,
			},
		},
	}

	result, err := ReplayRedfishFromRawPayloads(rawPayloads, nil)
	if err != nil {
		t.Fatalf("ReplayRedfishFromRawPayloads() failed: %v", err)
	}
	if len(result.Hardware.Storage) != 1 || result.Hardware.Storage[0].Details == nil {
		t.Fatalf("expected storage details from replay drive metrics, got %+v", result.Hardware.Storage)
	}
	if result.Hardware.Storage[0].Details["power_on_hours"] != int64(1001) || result.Hardware.Storage[0].Details["media_errors"] != int64(3) {
		t.Fatalf("expected drive metrics counters, got %#v", result.Hardware.Storage[0].Details)
	}
	if result.Hardware.Storage[0].Details["available_spare_pct"] != 92.0 || result.Hardware.Storage[0].Details["temperature_c"] != 37.0 {
		t.Fatalf("expected drive metrics telemetry, got %#v", result.Hardware.Storage[0].Details)
	}
}

func TestRedfishCollectionMemberRefs_IncludesOemPublicMembers(t *testing.T) {
	collection := map[string]interface{}{
		"Members": []interface{}{
			map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/OB01"},
		},
		"Oem": map[string]interface{}{
			"Public": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/FP00HDD00"},
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/FP00HDD02"},
				},
			},
		},
	}

	got := redfishCollectionMemberRefs(collection)
	if len(got) != 3 {
		t.Fatalf("expected 3 member refs, got %d: %v", len(got), got)
	}
}

func TestParseDriveAndPSU_CollectComponentMetricsIntoDetails(t *testing.T) {
	drive := parseDrive(map[string]interface{}{
		"Id":                            "Drive0",
		"Model":                         "NVMe SSD",
		"SerialNumber":                  "SSD-001",
		"TemperatureCelsius":            38.5,
		"PowerOnHours":                  12450,
		"UnsafeShutdowns":               3,
		"PredictedMediaLifeLeftPercent": 91,
		"Oem": map[string]interface{}{
			"Public": map[string]interface{}{
				"AvailableSparePercent": 87,
			},
		},
	})
	if drive.Details == nil {
		t.Fatalf("expected drive details to be populated")
	}
	if got := drive.Details["temperature_c"]; got != 38.5 {
		t.Fatalf("expected drive temperature detail 38.5, got %#v", got)
	}
	if got := drive.Details["power_on_hours"]; got != int64(12450) {
		t.Fatalf("expected drive power_on_hours detail, got %#v", got)
	}
	if got := drive.Details["life_remaining_pct"]; got != 91.0 {
		t.Fatalf("expected drive life_remaining_pct detail, got %#v", got)
	}
	if got := drive.Details["available_spare_pct"]; got != 87.0 {
		t.Fatalf("expected drive available_spare_pct detail from Oem/Public, got %#v", got)
	}

	driveOEM := parseDrive(map[string]interface{}{
		"Id":           "Drive1",
		"Model":        "NVMe SSD 2",
		"SerialNumber": "SSD-002",
		"Oem": map[string]interface{}{
			"Public": map[string]interface{}{
				"temperature":           19,
				"PercentAvailableSpare": 93,
				"PercentageUsed":        7,
			},
		},
	})
	if driveOEM.Details == nil {
		t.Fatalf("expected oem drive details to be populated")
	}
	if got := driveOEM.Details["temperature_c"]; got != 19.0 {
		t.Fatalf("expected lowercase OEM drive temperature 19, got %#v", got)
	}
	if got := driveOEM.Details["available_spare_pct"]; got != 93.0 {
		t.Fatalf("expected OEM available_spare_pct 93, got %#v", got)
	}
	if got := driveOEM.Details["life_used_pct"]; got != 7.0 {
		t.Fatalf("expected OEM life_used_pct 7, got %#v", got)
	}

	psu := parsePSU(map[string]interface{}{
		"MemberId":           "PSU0",
		"SerialNumber":       "PSU-001",
		"TemperatureCelsius": 41,
		"Oem": map[string]interface{}{
			"Public": map[string]interface{}{
				"LifeRemainingPercent": 96,
			},
		},
	}, 1)
	if psu.Details == nil {
		t.Fatalf("expected psu details to be populated")
	}
	if got := psu.Details["temperature_c"]; got != 41.0 {
		t.Fatalf("expected psu temperature detail 41, got %#v", got)
	}
	if got := psu.Details["life_remaining_pct"]; got != 96.0 {
		t.Fatalf("expected psu life_remaining_pct detail, got %#v", got)
	}
}

func TestParseGPUPCIeAndNIC_CollectComponentMetricsIntoDetails(t *testing.T) {
	functionDocs := []map[string]interface{}{
		{
			"FunctionId":            "0000:17:00.0",
			"VendorId":              "0x10de",
			"DeviceId":              "0x2331",
			"TemperatureCelsius":    48.5,
			"PowerConsumedWatts":    315.0,
			"ECCCorrectedTotal":     12,
			"BatteryHealthPercent":  87,
			"SFPTemperatureCelsius": 36.2,
		},
	}
	gpu := parseGPU(map[string]interface{}{
		"Id":           "GPU0",
		"Model":        "NVIDIA H100",
		"Manufacturer": "NVIDIA",
	}, functionDocs, 1)
	if gpu.Details == nil || gpu.Details["temperature_c"] != 48.5 || gpu.Details["power_w"] != 315.0 {
		t.Fatalf("expected gpu details from function docs, got %#v", gpu.Details)
	}

	pcie := parsePCIeDevice(map[string]interface{}{
		"Id": "NIC1",
	}, []map[string]interface{}{
		{
			"FunctionId":             "0000:18:00.0",
			"VendorId":               "0x15b3",
			"DeviceId":               "0x1021",
			"SFPTXPowerDBm":          -1.8,
			"SFPRXPowerDBm":          -2.1,
			"SFPBiasMA":              5.5,
			"BatteryReplaceRequired": true,
		},
	})
	if pcie.Details == nil || pcie.Details["sfp_tx_power_dbm"] != -1.8 || pcie.Details["battery_replace_required"] != true {
		t.Fatalf("expected pcie details from function docs, got %#v", pcie.Details)
	}

	nic := parseNIC(map[string]interface{}{"Id": "1"})
	enrichNICFromPCIe(&nic, map[string]interface{}{}, []map[string]interface{}{
		{
			"FunctionId":            "0000:19:00.0",
			"SFPTemperatureCelsius": 34.0,
		},
	}, nil)
	if nic.Details == nil || nic.Details["sfp_temperature_c"] != 34.0 {
		t.Fatalf("expected nic details from linked pcie function, got %#v", nic.Details)
	}
}

func TestParseComponentDetails_UseLinkedSupplementalMetrics(t *testing.T) {
	drive := parseDriveWithSupplementalDocs(
		map[string]interface{}{
			"Id":           "Drive0",
			"SerialNumber": "SSD-001",
		},
		map[string]interface{}{
			"PowerOnHours":    5001,
			"MediaErrors":     2,
			"TemperatureC":    39.5,
			"LifeUsedPercent": 9,
		},
	)
	if drive.Details == nil || drive.Details["power_on_hours"] != int64(5001) || drive.Details["temperature_c"] != 39.5 {
		t.Fatalf("expected drive details from supplemental metrics, got %#v", drive.Details)
	}

	psu := parsePSUWithSupplementalDocs(
		map[string]interface{}{
			"MemberId":     "PSU0",
			"SerialNumber": "PSU-001",
		},
		1,
		map[string]interface{}{
			"Temperature":          44,
			"LifeRemainingPercent": 97,
		},
	)
	if psu.Details == nil || psu.Details["temperature_c"] != 44.0 || psu.Details["life_remaining_pct"] != 97.0 {
		t.Fatalf("expected psu details from supplemental metrics, got %#v", psu.Details)
	}

	gpu := parseGPUWithSupplementalDocs(
		map[string]interface{}{
			"Id":           "GPU0",
			"Model":        "NVIDIA H100",
			"Manufacturer": "NVIDIA",
		},
		nil,
		[]map[string]interface{}{
			{
				"PowerConsumptionWatts": 305.0,
				"HWSlowdown":            true,
			},
		},
		1,
	)
	if gpu.Details == nil || gpu.Details["power_w"] != 305.0 || gpu.Details["hw_slowdown"] != true {
		t.Fatalf("expected gpu details from supplemental metrics, got %#v", gpu.Details)
	}
}

func TestRecoverCriticalRedfishDocsPlanB_RetriesMembersFromExistingCollection(t *testing.T) {
	t.Setenv("LOGPILE_REDFISH_CRITICAL_COOLDOWN", "0s")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_SLOW_GAP", "0s")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_PLANB_RETRIES", "1")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_RETRIES", "1")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_BACKOFF", "0s")

	const memberPath = "/redfish/v1/Chassis/1/Drives/FP00HDD00"
	mux := http.NewServeMux()
	mux.HandleFunc(memberPath, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]interface{}{
			"Id":            "FP00HDD00",
			"Name":          "FP00HDD00",
			"Model":         "HDD-TEST",
			"MediaType":     "HDD",
			"Protocol":      "SAS",
			"CapacityBytes": int64(2000398934016),
			"SerialNumber":  "HDD-SN-001",
		})
	})
	ts := httptest.NewServer(mux)
	defer ts.Close()

	rawTree := map[string]interface{}{
		"/redfish/v1/Chassis/1/Drives": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/OB01"},
			},
			"Oem": map[string]interface{}{
				"Public": map[string]interface{}{
					"Members": []interface{}{
						map[string]interface{}{"@odata.id": memberPath},
					},
				},
			},
		},
	}
	fetchErrs := map[string]string{
		memberPath: "Get \"https://example/redfish/v1/Chassis/1/Drives/FP00HDD00\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)",
	}

	c := NewRedfishConnector()
	recovered := c.recoverCriticalRedfishDocsPlanB(
		context.Background(),
		ts.Client(),
		Request{},
		ts.URL,
		[]string{"/redfish/v1/Chassis/1/Drives"},
		rawTree,
		fetchErrs,
		redfishprofile.AcquisitionTuning{
			RecoveryPolicy: redfishprofile.AcquisitionRecoveryPolicy{
				EnableCriticalCollectionMemberRetry: true,
				EnableCriticalSlowProbe:             true,
			},
		},
		nil,
	)
	if recovered == 0 {
		t.Fatalf("expected plan-B to recover at least one document")
	}
	if _, ok := rawTree[memberPath]; !ok {
		t.Fatalf("expected recovered member doc for %s", memberPath)
	}
	if _, ok := fetchErrs[memberPath]; ok {
		t.Fatalf("expected fetch error for %s to be cleared after recovery", memberPath)
	}
}

func TestRecoverCriticalRedfishDocsPlanB_RetriesMembersFromSystemMemoryCollection(t *testing.T) {
	t.Setenv("LOGPILE_REDFISH_CRITICAL_COOLDOWN", "0s")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_SLOW_GAP", "0s")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_PLANB_RETRIES", "1")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_RETRIES", "1")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_BACKOFF", "0s")

	const systemPath = "/redfish/v1/Systems/1"
	const memoryPath = "/redfish/v1/Systems/1/Memory"
	const dimmPath = "/redfish/v1/Systems/1/Memory/CPU1_C1D1"

	mux := http.NewServeMux()
	mux.HandleFunc(dimmPath, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]interface{}{
			"Id":               "CPU1_C1D1",
			"Name":             "CPU1_C1D1",
			"DeviceLocator":    "CPU1_C1D1",
			"CapacityMiB":      65536,
			"MemoryDeviceType": "DDR5",
			"Status":           map[string]interface{}{"State": "Enabled", "Health": "OK"},
			"SerialNumber":     "DIMM-SN-001",
			"PartNumber":       "DIMM-PN-001",
		})
	})
	ts := httptest.NewServer(mux)
	defer ts.Close()

	rawTree := map[string]interface{}{
		memoryPath: map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": dimmPath},
			},
		},
	}
	fetchErrs := map[string]string{
		dimmPath: `Get "https://example/redfish/v1/Systems/1/Memory/CPU1_C1D1": context deadline exceeded (Client.Timeout exceeded while awaiting headers)`,
	}

	plan := redfishprofile.BuildAcquisitionPlan(redfishprofile.MatchSignals{})
	match := redfishprofile.MatchProfiles(redfishprofile.MatchSignals{})
	resolved := redfishprofile.ResolveAcquisitionPlan(match, plan, redfishprofile.DiscoveredResources{
		SystemPaths: []string{systemPath},
	}, redfishprofile.MatchSignals{})
	criticalPaths := resolved.CriticalPaths
	hasMemoryPath := false
	for _, p := range criticalPaths {
		if p == memoryPath {
			hasMemoryPath = true
			break
		}
	}
	if !hasMemoryPath {
		t.Fatalf("expected critical endpoints to include %s", memoryPath)
	}

	c := NewRedfishConnector()
	recovered := c.recoverCriticalRedfishDocsPlanB(
		context.Background(),
		ts.Client(),
		Request{},
		ts.URL,
		criticalPaths,
		rawTree,
		fetchErrs,
		redfishprofile.AcquisitionTuning{
			RecoveryPolicy: redfishprofile.AcquisitionRecoveryPolicy{
				EnableCriticalCollectionMemberRetry: true,
				EnableCriticalSlowProbe:             true,
			},
		},
		nil,
	)
	if recovered == 0 {
		t.Fatalf("expected plan-B to recover at least one DIMM document")
	}
	if _, ok := rawTree[dimmPath]; !ok {
		t.Fatalf("expected recovered DIMM doc for %s", dimmPath)
	}
	if _, ok := fetchErrs[dimmPath]; ok {
		t.Fatalf("expected DIMM fetch error for %s to be cleared", dimmPath)
	}
}

func TestRecoverCriticalRedfishDocsPlanB_SkipsMemberRetryWithoutRecoveryPolicy(t *testing.T) {
	t.Setenv("LOGPILE_REDFISH_CRITICAL_COOLDOWN", "0s")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_SLOW_GAP", "0s")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_PLANB_RETRIES", "1")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_RETRIES", "1")
	t.Setenv("LOGPILE_REDFISH_CRITICAL_BACKOFF", "0s")

	const memoryPath = "/redfish/v1/Systems/1/Memory"
	const dimmPath = "/redfish/v1/Systems/1/Memory/CPU1_C1D1"

	rawTree := map[string]interface{}{
		memoryPath: map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": dimmPath},
			},
		},
	}
	fetchErrs := map[string]string{
		dimmPath: `Get "https://example/redfish/v1/Systems/1/Memory/CPU1_C1D1": context deadline exceeded (Client.Timeout exceeded while awaiting headers)`,
	}

	c := NewRedfishConnector()
	recovered := c.recoverCriticalRedfishDocsPlanB(
		context.Background(),
		http.DefaultClient,
		Request{},
		"https://example",
		[]string{memoryPath},
		rawTree,
		fetchErrs,
		redfishprofile.AcquisitionTuning{},
		nil,
	)
	if recovered != 0 {
		t.Fatalf("expected no recovery without recovery policy, got %d", recovered)
	}
	if _, ok := rawTree[dimmPath]; ok {
		t.Fatalf("did not expect recovered DIMM doc for %s", dimmPath)
	}
	if _, ok := fetchErrs[dimmPath]; !ok {
		t.Fatalf("expected DIMM fetch error for %s to remain", dimmPath)
	}
}

func TestReplayCollectStorage_ProbesSupermicroNVMeDiskBayWhenCollectionEmpty(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Systems": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
			},
		},
		"/redfish/v1/Systems/1/Storage": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/NVMeSSD"},
			},
		},
		"/redfish/v1/Systems/1/Storage/NVMeSSD": map[string]interface{}{
			"Drives": map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/NVMeSSD/Drives"},
		},
		"/redfish/v1/Systems/1/Storage/NVMeSSD/Drives": map[string]interface{}{
			"Members": []interface{}{},
		},
		"/redfish/v1/Chassis": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane"},
			},
		},
		"/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane": map[string]interface{}{
			"Drives": map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives"},
		},
		"/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives": map[string]interface{}{
			"Members@odata.count": 0,
			"Members":             []interface{}{},
		},
		"/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives/Disk.Bay.0": map[string]interface{}{
			"Id":            "Disk.Bay.0",
			"Name":          "Disk.Bay.0",
			"Manufacturer":  "INTEL",
			"SerialNumber":  "BTLJ035203XT1P0FGN",
			"Model":         "INTEL SSDPE2KX010T8",
			"CapacityBytes": int64(1000204886016),
			"Protocol":      "NVMe",
			"MediaType":     "SSD",
			"Status":        map[string]interface{}{"State": "Enabled", "Health": "OK"},
		},
	}}

	got := r.collectStorage("/redfish/v1/Systems/1", testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableSupermicroNVMeBackplane: true}))
	if len(got) != 1 {
		t.Fatalf("expected one drive from direct Disk.Bay probe, got %d", len(got))
	}
	if got[0].SerialNumber != "BTLJ035203XT1P0FGN" {
		t.Fatalf("unexpected serial: %q", got[0].SerialNumber)
	}
	if got[0].SizeGB == 0 {
		t.Fatalf("expected size to be parsed from CapacityBytes")
	}
}

func TestReplayCollectStorage_SkipsEnclosureRecoveryWhenDirectiveDisabled(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Systems/1/Storage": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/1"},
			},
		},
		"/redfish/v1/Systems/1/Storage/1": map[string]interface{}{
			"Links": map[string]interface{}{
				"Enclosures": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Enclosures/1"},
				},
			},
		},
		"/redfish/v1/Enclosures/1/Drives": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Enclosures/1/Drives/Drive1"},
			},
		},
		"/redfish/v1/Enclosures/1/Drives/Drive1": map[string]interface{}{
			"Id":           "Drive1",
			"Name":         "Drive1",
			"Model":        "INTEL SSD",
			"SerialNumber": "ENCLOSURE-DRIVE-001",
			"Protocol":     "SATA",
			"MediaType":    "SSD",
		},
	}}

	got := r.collectStorage("/redfish/v1/Systems/1", testAnalysisPlan(redfishprofile.AnalysisDirectives{}))
	if len(got) != 0 {
		t.Fatalf("expected no enclosure recovery when directive is off, got %d", len(got))
	}
}

func TestReplayCollectStorage_UsesKnownControllerRecoveryWhenEnabled(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Systems/1/Storage/IntelVROC/Drives": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/IntelVROC/Drives/1"},
			},
		},
		"/redfish/v1/Systems/1/Storage/IntelVROC/Drives/1": map[string]interface{}{
			"Id":           "1",
			"Name":         "Drive1",
			"Model":        "VROC SSD",
			"SerialNumber": "VROC-001",
			"Protocol":     "NVMe",
			"MediaType":    "SSD",
		},
	}}

	got := r.collectStorage("/redfish/v1/Systems/1", redfishprofile.ResolvedAnalysisPlan{
		Directives:                   redfishprofile.AnalysisDirectives{EnableKnownStorageControllerRecovery: true},
		KnownStorageDriveCollections: []string{"/Storage/IntelVROC/Drives"},
	})
	if len(got) != 1 {
		t.Fatalf("expected one drive from known controller recovery, got %d", len(got))
	}
	if got[0].SerialNumber != "VROC-001" {
		t.Fatalf("unexpected serial %q", got[0].SerialNumber)
	}
}

func TestReplayCollectStorageVolumes_SkipsVolumeCapabilitiesFallbackMember(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Systems/1/Storage": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/DE00A000"},
			},
		},
		"/redfish/v1/Systems/1/Storage/DE00A000": map[string]interface{}{
			"Id":      "DE00A000",
			"Volumes": map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/DE00A000/Volumes"},
		},
		"/redfish/v1/Systems/1/Storage/DE00A000/Volumes": map[string]interface{}{
			"@odata.id":           "/redfish/v1/Systems/1/Storage/DE00A000/Volumes",
			"@odata.type":         "#VolumeCollection.VolumeCollection",
			"Members":             []interface{}{},
			"Members@odata.count": 0,
			"Name":                "MR Volume Collection",
		},
		"/redfish/v1/Systems/1/Storage/DE00A000/Volumes/Capabilities": map[string]interface{}{
			"@odata.id":   "/redfish/v1/Systems/1/Storage/DE00A000/Volumes/Capabilities",
			"@odata.type": "#Volume.v1_9_0.Volume",
			"Id":          "Capabilities",
			"Name":        "Capabilities for VolumeCollection",
		},
	}}

	got := r.collectStorageVolumes("/redfish/v1/Systems/1", testAnalysisPlan(redfishprofile.AnalysisDirectives{}))
	if len(got) != 0 {
		t.Fatalf("expected capabilities-only volume collection to stay empty, got %+v", got)
	}
}

func TestReplayCollectPCIeDevices_UsesChassisDeviceSupplementalDocs(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/2"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/2": map[string]interface{}{
			"@odata.id":    "/redfish/v1/Chassis/1/PCIeDevices/2",
			"Name":         "BCM 5719 1Gb 4p BASE-T OCP Adptr",
			"Model":        "P51183-001",
			"PartNumber":   "P51183-001",
			"Manufacturer": "Broadcom",
			"SerialNumber": "1CH0150001",
			"DeviceType":   "SingleFunction",
		},
		"/redfish/v1/Chassis/1/Devices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Devices/2"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Devices/4"},
			},
		},
		"/redfish/v1/Chassis/1/Devices/2": map[string]interface{}{
			"@odata.id":         "/redfish/v1/Chassis/1/Devices/2",
			"Name":              "BCM 5719 1Gb 4p BASE-T OCP Adptr",
			"DeviceType":        "LOM/NIC",
			"Manufacturer":      "Broadcom",
			"PartNumber":        "BCM95719N1905HC",
			"ProductPartNumber": "P51183-001",
			"SerialNumber":      "1CH0150001",
			"Location":          "OCP 3.0 Slot 15",
		},
		"/redfish/v1/Chassis/1/Devices/4": map[string]interface{}{
			"@odata.id":    "/redfish/v1/Chassis/1/Devices/4",
			"Name":         "Empty slot 2",
			"DeviceType":   "Unknown",
			"Location":     "PCI-E Slot 2",
			"SerialNumber": "",
		},
	}}

	got := r.collectPCIeDevices(nil, []string{"/redfish/v1/Chassis/1"})
	if len(got) != 1 {
		t.Fatalf("expected one PCIe device, got %d", len(got))
	}
	if got[0].Slot != "OCP 3.0 Slot 15" {
		t.Fatalf("expected chassis device location to override weak slot label, got %+v", got[0])
	}
	if got[0].DeviceClass != "LOM/NIC" {
		t.Fatalf("expected chassis device type to enrich class, got %+v", got[0])
	}
	if got[0].DeviceClass == "P51183-001" {
		t.Fatalf("device class should not degrade into part number: %+v", got[0])
	}
}

func TestReplayCollectGPUs_DoesNotCollapseOnPlaceholderSerialAndSkipsNIC(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/3"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/9"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/7"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/3": map[string]interface{}{
			"Id":           "3",
			"Name":         "PCIeCard3",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "N/A",
			"Oem": map[string]interface{}{
				"Public": map[string]interface{}{
					"DeviceClass": "DisplayController",
				},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/9": map[string]interface{}{
			"Id":           "9",
			"Name":         "PCIeCard9",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "N/A",
			"Oem": map[string]interface{}{
				"Public": map[string]interface{}{
					"DeviceClass": "DisplayController",
				},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/7": map[string]interface{}{
			"Id":           "7",
			"Name":         "PCIeCard7",
			"Model":        "MCX631102AN-ADAT",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "MT2538J00CZE",
			"Oem": map[string]interface{}{
				"Public": map[string]interface{}{
					"DeviceClass": "NetworkController",
				},
			},
		},
	}}

	got := r.collectGPUs(nil, []string{"/redfish/v1/Chassis/1"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	if len(got) != 2 {
		t.Fatalf("expected 2 GPUs (two H200 cards), got %d", len(got))
	}
	for _, gpu := range got {
		if gpu.Model == "MCX631102AN-ADAT" {
			t.Fatalf("network adapter should not be classified as GPU")
		}
	}
}

func TestReplayCollectPCIeDevices_SkipsMSITopologyNoiseClasses(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/bridge"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/processor"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/signal"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/serial"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/display"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/network"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/storage"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/bridge": map[string]interface{}{
			"Id":           "bridge",
			"Name":         "Bridge",
			"Description":  "Bridge Device",
			"Manufacturer": "Intel Corporation",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/bridge/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/bridge/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/bridge/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/bridge/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "Bridge",
			"VendorId":    "0x8086",
			"DeviceId":    "0x0db0",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/processor": map[string]interface{}{
			"Id":           "processor",
			"Name":         "Processor",
			"Description":  "Processor Device",
			"Manufacturer": "Intel Corporation",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/processor/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/processor/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/processor/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/processor/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "Processor",
			"VendorId":    "0x8086",
			"DeviceId":    "0x4944",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/signal": map[string]interface{}{
			"Id":           "signal",
			"Name":         "Signal",
			"Manufacturer": "Intel Corporation",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/signal/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/signal/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/signal/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/signal/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "SignalProcessingController",
			"VendorId":    "0x8086",
			"DeviceId":    "0x3254",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/serial": map[string]interface{}{
			"Id":           "serial",
			"Name":         "Serial",
			"Manufacturer": "Renesas",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/serial/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/serial/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/serial/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/serial/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "SerialBusController",
			"VendorId":    "0x1912",
			"DeviceId":    "0x0014",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/display": map[string]interface{}{
			"Id":           "display",
			"Name":         "Display",
			"Description":  "Display Device",
			"Manufacturer": "NVIDIA Corporation",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/display/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/display/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/display/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/display/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "DisplayController",
			"VendorId":    "0x10de",
			"DeviceId":    "0x233b",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/network": map[string]interface{}{
			"Id":           "network",
			"Name":         "NIC",
			"Description":  "Network Device",
			"Manufacturer": "Mellanox Technologies",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/network/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/network/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/network/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/network/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "NetworkController",
			"VendorId":    "0x15b3",
			"DeviceId":    "0x101f",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/storage": map[string]interface{}{
			"Id":           "storage",
			"Name":         "Storage",
			"Description":  "Storage Device",
			"Manufacturer": "Intel Corporation",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/storage/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/storage/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/storage/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/storage/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "MassStorageController",
			"VendorId":    "0x1234",
			"DeviceId":    "0x5678",
		},
	}}

	got := r.collectPCIeDevices(nil, []string{"/redfish/v1/Chassis/1"})
	if len(got) != 2 {
		t.Fatalf("expected only endpoint PCIe devices to remain, got %d: %+v", len(got), got)
	}
	classes := map[string]bool{}
	for _, dev := range got {
		classes[dev.DeviceClass] = true
	}
	if !classes["NetworkController"] || !classes["MassStorageController"] {
		t.Fatalf("expected network and storage PCIe devices to remain, got %+v", got)
	}
	if classes["Bridge"] || classes["Processor"] || classes["SignalProcessingController"] || classes["SerialBusController"] || classes["DisplayController"] {
		t.Fatalf("expected MSI topology noise classes to be filtered, got %+v", got)
	}
}

func TestReplayCollectPCIeDevices_SkipsNICsAlreadyRepresentedAsNetworkAdapters(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/nic"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/nic": map[string]interface{}{
			"Id":           "nic",
			"Name":         "PCIeDevice_00_39_00",
			"Description":  "Network Device",
			"Manufacturer": "Mellanox Technologies",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/nic/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/nic/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/nic/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/nic/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "NetworkController",
			"VendorId":    "0x15b3",
			"DeviceId":    "0x101f",
			"Links": map[string]interface{}{
				"NetworkDeviceFunctions": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/NIC1/NetworkDeviceFunctions/Function0"},
				},
				"NetworkDeviceFunctions@odata.count": 1,
			},
		},
	}}

	got := r.collectPCIeDevices(nil, []string{"/redfish/v1/Chassis/1"})
	if len(got) != 0 {
		t.Fatalf("expected network-backed PCIe duplicate to be skipped, got %+v", got)
	}
}

func TestReplayCollectPCIeDevices_SkipsStorageServiceEndpoints(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/vmd"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/switch-mgmt"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/hba"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/vmd": map[string]interface{}{
			"Id":          "vmd",
			"Description": "Storage Device",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/vmd/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/vmd/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/vmd/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/vmd/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "MassStorageController",
			"VendorId":    "0x8086",
			"DeviceId":    "0x28c0",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/switch-mgmt": map[string]interface{}{
			"Id":          "switch-mgmt",
			"Description": "Storage Device",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/switch-mgmt/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/switch-mgmt/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/switch-mgmt/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/switch-mgmt/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "MassStorageController",
			"VendorId":    "0x1000",
			"DeviceId":    "0x00b2",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/hba": map[string]interface{}{
			"Id":          "hba",
			"Description": "Storage Device",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/hba/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/hba/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/hba/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/hba/PCIeFunctions/1": map[string]interface{}{
			"DeviceClass": "MassStorageController",
			"VendorId":    "0x1234",
			"DeviceId":    "0x5678",
		},
	}}

	got := r.collectPCIeDevices(nil, []string{"/redfish/v1/Chassis/1"})
	if len(got) != 1 {
		t.Fatalf("expected only non-service storage controller to remain, got %+v", got)
	}
	if got[0].VendorID != 0x1234 || got[0].DeviceID != 0x5678 {
		t.Fatalf("expected generic HBA to remain, got %+v", got[0])
	}
}

func TestParseBoardInfo_NormalizesNullPlaceholders(t *testing.T) {
	got := parseBoardInfo(map[string]interface{}{
		"Manufacturer": "NULL",
		"Model":        "NULL",
		"SerialNumber": "23E100051",
		"PartNumber":   "0 ",
		"UUID":         "fa403f6f-2ee9-11f0-bab9-346f1104085a",
	})
	if got.Manufacturer != "" {
		t.Fatalf("expected empty manufacturer, got %q", got.Manufacturer)
	}
	if got.ProductName != "" {
		t.Fatalf("expected empty product name, got %q", got.ProductName)
	}
	if got.PartNumber != "" {
		t.Fatalf("expected empty part number, got %q", got.PartNumber)
	}
	if got.SerialNumber != "23E100051" {
		t.Fatalf("unexpected serial number: %q", got.SerialNumber)
	}
}

func TestParseBoardInfo_UsesSKUAsPartNumberFallback(t *testing.T) {
	got := parseBoardInfo(map[string]interface{}{
		"Manufacturer": "HPE",
		"Model":        "ProLiant DL380 Gen11",
		"SerialNumber": "CZ2D1X0GS4",
		"SKU":          "P52560-421",
	})
	if got.PartNumber != "P52560-421" {
		t.Fatalf("expected SKU to populate part number, got %q", got.PartNumber)
	}
}

func TestShouldCrawlPath_SkipsJsonSchemas(t *testing.T) {
	if shouldCrawlPath("/redfish/v1/JsonSchemas") {
		t.Fatalf("expected /JsonSchemas to be skipped")
	}
	if shouldCrawlPath("/redfish/v1/JsonSchemas/ComputerSystem.v1_8_0") {
		t.Fatalf("expected JsonSchemas members to be skipped")
	}
	if !shouldCrawlPath("/redfish/v1/Systems/1") {
		t.Fatalf("expected normal hardware path to be crawled")
	}
}

func TestReplayCollectGPUs_FromGraphicsControllers(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU0"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU1"},
			},
		},
		"/redfish/v1/Systems/1/GraphicsControllers/GPU0": map[string]interface{}{
			"Id":           "GPU0",
			"Name":         "GPU0",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "1654225094493",
			"Status":       map[string]interface{}{"State": "Enabled", "Health": "OK"},
		},
		"/redfish/v1/Systems/1/GraphicsControllers/GPU1": map[string]interface{}{
			"Id":           "GPU1",
			"Name":         "GPU1",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "1654425002635",
			"Status":       map[string]interface{}{"State": "Enabled", "Health": "OK"},
		},
	}}

	got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, nil, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	if len(got) != 2 {
		t.Fatalf("expected 2 GPUs from GraphicsControllers, got %d", len(got))
	}
	if got[0].SerialNumber == "" || got[1].SerialNumber == "" {
		t.Fatalf("expected GPU serial numbers from GraphicsControllers")
	}
}

func TestReplayCollectGPUs_DedupUsesRedfishPathBeforeHeuristics(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU0"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU1"},
			},
		},
		"/redfish/v1/Systems/1/GraphicsControllers/GPU0": map[string]interface{}{
			"Id":           "GPU0",
			"Name":         "H100-PCIE-80G",
			"Model":        "H100-PCIE-80G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "N/A",
		},
		"/redfish/v1/Systems/1/GraphicsControllers/GPU1": map[string]interface{}{
			"Id":           "GPU1",
			"Name":         "H100-PCIE-80G",
			"Model":        "H100-PCIE-80G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "N/A",
		},
	}}

	got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, nil, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	if len(got) != 2 {
		t.Fatalf("expected both GPUs to be kept by unique redfish path, got %d", len(got))
	}
}

func TestParseGPU_xFusionPCIeInterfaceMaxlanes(t *testing.T) {
	// xFusion GPU PCIeDevices (PCIeCard1..N) carry link width in PCIeInterface
	// with "Maxlanes" (lowercase 'l') rather than "MaxLanes".
	doc := map[string]interface{}{
		"Id":    "PCIeCard1",
		"Model": "RTX PRO 6000",
		"PCIeInterface": map[string]interface{}{
			"LanesInUse":  16,
			"Maxlanes":    16,
			"PCIeType":    "Gen5",
			"MaxPCIeType": "Gen5",
		},
	}
	gpu := parseGPU(doc, nil, 1)
	if gpu.CurrentLinkWidth != 16 || gpu.MaxLinkWidth != 16 {
		t.Fatalf("expected link widths 16/16 from PCIeInterface, got current=%d max=%d", gpu.CurrentLinkWidth, gpu.MaxLinkWidth)
	}
	if gpu.CurrentLinkSpeed != "Gen5" || gpu.MaxLinkSpeed != "Gen5" {
		t.Fatalf("expected link speeds Gen5/Gen5 from PCIeInterface, got current=%q max=%q", gpu.CurrentLinkSpeed, gpu.MaxLinkSpeed)
	}
}

func TestParseGPU_UsesNestedOemSerialNumber(t *testing.T) {
	doc := map[string]interface{}{
		"Id":           "GPU4",
		"Name":         "H100-PCIE-80G",
		"Model":        "H100-PCIE-80G",
		"Manufacturer": "NVIDIA",
		"SerialNumber": "N/A",
		"Oem": map[string]interface{}{
			"SerialNumber": "1794024010533",
		},
	}

	got := parseGPU(doc, nil, 1)
	if got.SerialNumber != "1794024010533" {
		t.Fatalf("expected nested OEM serial number, got %q", got.SerialNumber)
	}
}

func TestParseBoardInfoWithFallback_UsesFRU(t *testing.T) {
	system := map[string]interface{}{
		"Manufacturer": "NULL",
		"Model":        "NULL",
		"SerialNumber": "23E100051",
		"PartNumber":   "0",
	}
	chassis := map[string]interface{}{
		"Manufacturer": "NULL",
		"Model":        "NULL",
	}
	fru := map[string]interface{}{
		"FRUInfo": map[string]interface{}{
			"Board": map[string]interface{}{
				"Manufacturer": "Kaytus",
				"ProductName":  "KR4268X2",
			},
		},
	}

	got := parseBoardInfoWithFallback(system, chassis, fru)
	if got.ProductName != "KR4268X2" {
		t.Fatalf("expected product from FRU, got %q", got.ProductName)
	}
	if got.Manufacturer != "Kaytus" {
		t.Fatalf("expected manufacturer from FRU, got %q", got.Manufacturer)
	}
	if got.SerialNumber != "23E100051" {
		t.Fatalf("expected serial from system, got %q", got.SerialNumber)
	}
}

func TestReplayRedfishFromRawPayloads_AddsMissingServerModelWarning(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
				},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Manufacturer": "NULL",
				"Model":        "NULL",
				"SerialNumber": "23E100051",
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"},
				},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{
				"Id":           "1",
				"Manufacturer": "NULL",
				"Model":        "NULL",
			},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"},
				},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{
				"Id": "1",
			},
		},
		"redfish_fetch_errors": []map[string]interface{}{
			{"path": "/redfish/v1/Systems/1/Oem/Public/FRU", "error": "status 500"},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}
	if got.Hardware == nil {
		t.Fatalf("expected hardware")
	}
	if got.Hardware.BoardInfo.ProductName != "" {
		t.Fatalf("expected empty model for warning test, got %q", got.Hardware.BoardInfo.ProductName)
	}
	found := false
	for _, ev := range got.Events {
		if ev.Source == "Redfish" && ev.EventType == "Collection Warning" {
			found = true
			break
		}
	}
	if !found {
		t.Fatalf("expected collection warning event about missing server model")
	}
}

func TestReplayRedfishFromRawPayloads_StoresAnalysisProfilesMetadata(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Vendor":   "AMI",
				"Product":  "AMI Redfish",
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
				},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Manufacturer": "Micro-Star International Co., Ltd.",
				"Model":        "CG290",
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"},
				},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{
				"Manufacturer": "Micro-Star International Co., Ltd.",
				"Model":        "CG290",
			},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"},
				},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{
				"Id": "1",
			},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}
	meta, ok := got.RawPayloads["redfish_analysis_profiles"].(map[string]any)
	if !ok {
		t.Fatalf("expected redfish_analysis_profiles metadata")
	}
	if meta["mode"] != redfishprofile.ModeMatched {
		t.Fatalf("expected matched mode, got %#v", meta["mode"])
	}
	profiles, ok := meta["profiles"].([]string)
	if !ok {
		t.Fatalf("expected []string profiles, got %T", meta["profiles"])
	}
	foundMSI := false
	for _, profile := range profiles {
		if profile == "msi" {
			foundMSI = true
			break
		}
	}
	if !foundMSI {
		t.Fatalf("expected msi in applied profiles, got %v", profiles)
	}
	planMeta, ok := got.RawPayloads["redfish_analysis_plan"].(map[string]any)
	if !ok {
		t.Fatalf("expected redfish_analysis_plan metadata")
	}
	directives, ok := planMeta["directives"].(map[string]any)
	if !ok {
		t.Fatalf("expected directives map in redfish_analysis_plan")
	}
	if directives["generic_graphics_controller_dedup"] != true {
		t.Fatalf("expected generic_graphics_controller_dedup directive, got %#v", directives["generic_graphics_controller_dedup"])
	}
}

func TestReplayRedfishFromRawPayloads_AddsDriveFetchWarning(t *testing.T) {
	raw := map[string]any{
		"redfish_tree": map[string]interface{}{
			"/redfish/v1": map[string]interface{}{
				"Systems":  map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
				"Chassis":  map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
				"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
			},
			"/redfish/v1/Systems": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
				},
			},
			"/redfish/v1/Systems/1": map[string]interface{}{
				"Manufacturer": "Inspur",
				"Model":        "NF5688M7",
				"SerialNumber": "23E100051",
			},
			"/redfish/v1/Chassis": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"},
				},
			},
			"/redfish/v1/Chassis/1": map[string]interface{}{
				"Id":           "1",
				"Manufacturer": "Inspur",
				"Model":        "NF5688M7",
			},
			"/redfish/v1/Managers": map[string]interface{}{
				"Members": []interface{}{
					map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"},
				},
			},
			"/redfish/v1/Managers/1": map[string]interface{}{
				"Id": "1",
			},
		},
		"redfish_fetch_errors": []map[string]interface{}{
			{
				"path":  "/redfish/v1/Chassis/1/Drives/FP00HDD00",
				"error": `Get "...": context deadline exceeded (Client.Timeout exceeded while awaiting headers)`,
			},
		},
	}

	got, err := ReplayRedfishFromRawPayloads(raw, nil)
	if err != nil {
		t.Fatalf("replay failed: %v", err)
	}

	found := false
	for _, ev := range got.Events {
		if ev.Source == "Redfish" &&
			ev.EventType == "Collection Warning" &&
			strings.Contains(strings.ToLower(ev.Description), "drive documents were unavailable") {
			found = true
			break
		}
	}
	if !found {
		t.Fatalf("expected collection warning event for drive fetch errors")
	}
}

func TestReplayCollectGPUs_SkipsModelOnlyDuplicateFromGraphicsControllers(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Systems/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/PCIeDevices/3"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/PCIeDevices/9"},
			},
		},
		"/redfish/v1/Systems/1/PCIeDevices/3": map[string]interface{}{
			"Id":           "3",
			"Name":         "PCIeCard3",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "1654225094493",
		},
		"/redfish/v1/Systems/1/PCIeDevices/9": map[string]interface{}{
			"Id":           "9",
			"Name":         "PCIeCard9",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "1654425002635",
		},
		"/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU0"},
			},
		},
		"/redfish/v1/Systems/1/GraphicsControllers/GPU0": map[string]interface{}{
			"Id":           "GPU0",
			"Name":         "H200-SXM5-141G",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "N/A",
		},
	}}

	got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, nil, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	if len(got) != 2 {
		t.Fatalf("expected 2 GPUs without generic duplicate, got %d", len(got))
	}
	for _, gpu := range got {
		if gpu.Slot == "H200-SXM5-141G" {
			t.Fatalf("unexpected model-only duplicate GPU row")
		}
	}
}

func TestReplayCollectGPUs_KeepsModelOnlyGraphicsDuplicateWhenDirectiveDisabled(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/4"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/9"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/4": map[string]interface{}{
			"Id":           "4",
			"Name":         "PCIeCard4",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "1654225094493",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/9": map[string]interface{}{
			"Id":           "9",
			"Name":         "PCIeCard9",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "1654425002635",
		},
		"/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU0"},
			},
		},
		"/redfish/v1/Systems/1/GraphicsControllers/GPU0": map[string]interface{}{
			"Id":           "GPU0",
			"Name":         "H200-SXM5-141G",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "N/A",
		},
	}}

	got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, []string{"/redfish/v1/Chassis/1"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{}))
	if len(got) != 3 {
		t.Fatalf("expected model-only graphics duplicate to remain when directive is off, got %d", len(got))
	}
}

func TestApplyBoardInfoFallbackFromDocs_SkipsComponentProductNames(t *testing.T) {
	board := models.BoardInfo{
		SerialNumber: "23E100051",
	}
	docs := []map[string]interface{}{
		{
			"Model":        "DDR5 DIMM",
			"Manufacturer": "DELTA",
			"SerialNumber": "802C1A2507D284B001",
		},
		{
			"PlatformId":   "NF5688M7",
			"Manufacturer": "Inspur",
			"PartNumber":   "YZMB-00001",
		},
	}

	applyBoardInfoFallbackFromDocs(&board, docs)
	if board.ProductName != "NF5688M7" {
		t.Fatalf("expected server model from fallback docs, got %q", board.ProductName)
	}
	if board.Manufacturer != "Inspur" {
		t.Fatalf("expected manufacturer from server fallback doc, got %q", board.Manufacturer)
	}
}

func TestDedupeStorage_IgnoresPlaceholderSerial(t *testing.T) {
	in := []models.Storage{
		{Slot: "OB01", Model: "N/A", SerialNumber: "N/A"},
		{Slot: "OB02", Model: "N/A", SerialNumber: "N/A"},
		{Slot: "OB03", Model: "N/A", SerialNumber: "N/A"},
		{Slot: "OB04", Model: "N/A", SerialNumber: "N/A"},
	}

	out := dedupeStorage(in)
	if len(out) != 4 {
		t.Fatalf("expected all placeholder-serial NVMe drives to be kept by slot key, got %d", len(out))
	}
}

func TestDedupeStorage_MergesPlaceholderSlotsWithRichDrivesByOrder(t *testing.T) {
	in := []models.Storage{
		{Slot: "PCIe8_RAID_Disk_1:0", Type: "SSD", Model: "SOLIDIGM SSDSC2K", SizeGB: 1787, SerialNumber: "S1", Present: true},
		{Slot: "PCIe8_RAID_Disk_1:1", Type: "SSD", Model: "SOLIDIGM SSDSC2K", SizeGB: 1787, SerialNumber: "S2", Present: true},
		{Slot: "PCIe8_RAID_Disk_1:2", Type: "SSD", Model: "SOLIDIGM SSDSC2K", SizeGB: 1787, SerialNumber: "S3", Present: true},
		{Slot: "OB01", Type: "NVMe", Model: "N/A", SerialNumber: "N/A", Present: true},
		{Slot: "OB02", Type: "NVMe", Model: "N/A", SerialNumber: "N/A", Present: true},
		{Slot: "OB03", Type: "NVMe", Model: "N/A", SerialNumber: "N/A", Present: true},
		{Slot: "OB04", Type: "NVMe", Model: "N/A", SerialNumber: "N/A", Present: true},
		{Slot: "FP00HDD00", Type: "NVMe", Model: "INTEL SSDPE2KE032T8", SizeGB: 2980, SerialNumber: "N1", Present: true},
		{Slot: "FP00HDD02", Type: "NVMe", Model: "INTEL SSDPE2KE032T8", SizeGB: 2980, SerialNumber: "N2", Present: true},
		{Slot: "FP00HDD04", Type: "NVMe", Model: "INTEL SSDPE2KE032T8", SizeGB: 2980, SerialNumber: "N3", Present: true},
		{Slot: "FP00HDD06", Type: "NVMe", Model: "INTEL SSDPE2KE032T8", SizeGB: 2980, SerialNumber: "N4", Present: true},
	}

	out := dedupeStorage(in)
	if len(out) != 7 {
		t.Fatalf("expected 7 rows after placeholder merge, got %d", len(out))
	}

	bySlot := make(map[string]models.Storage, len(out))
	for _, d := range out {
		bySlot[d.Slot] = d
		if strings.HasPrefix(d.Slot, "FP00HDD") {
			t.Fatalf("expected FP donor slot %q to be absorbed by placeholder slot", d.Slot)
		}
	}
	if bySlot["OB01"].SerialNumber != "N1" || bySlot["OB02"].SerialNumber != "N2" || bySlot["OB03"].SerialNumber != "N3" || bySlot["OB04"].SerialNumber != "N4" {
		t.Fatalf("expected OB slots to be enriched in order, got OB01=%q OB02=%q OB03=%q OB04=%q",
			bySlot["OB01"].SerialNumber, bySlot["OB02"].SerialNumber, bySlot["OB03"].SerialNumber, bySlot["OB04"].SerialNumber)
	}
	if bySlot["OB01"].Model != "INTEL SSDPE2KE032T8" || bySlot["OB01"].SizeGB != 2980 {
		t.Fatalf("expected OB01 to inherit rich model/size, got model=%q size=%d", bySlot["OB01"].Model, bySlot["OB01"].SizeGB)
	}
}

func TestDedupeNetworkAdapters_MergesBySlotAndKeepsRicherData(t *testing.T) {
	in := []models.NetworkAdapter{
		{
			Slot:    "NIC-A",
			Model:   "N/A",
			Vendor:  "",
			Present: true,
		},
		{
			Slot:         "NIC-A",
			Model:        "ConnectX-7",
			Vendor:       "NVIDIA",
			SerialNumber: "NICSN001",
			Firmware:     "28.41.2020",
			PortCount:    2,
			MACAddresses: []string{"00:11:22:33:44:55"},
			Present:      true,
		},
	}

	out := dedupeNetworkAdapters(in)
	if len(out) != 1 {
		t.Fatalf("expected merged single NIC row, got %d", len(out))
	}
	if out[0].SerialNumber != "NICSN001" || out[0].Model != "ConnectX-7" || out[0].Vendor != "NVIDIA" {
		t.Fatalf("expected richer NIC fields preserved, got %+v", out[0])
	}
}

func TestDedupePCIeDevices_MergesByLooseKeyAndKeepsBDF(t *testing.T) {
	in := []models.PCIeDevice{
		{
			Slot:        "PCIe Slot 3",
			DeviceClass: "Network Controller",
			PartNumber:  "MCX75310AAS-NEAT",
		},
		{
			Slot:         "PCIe Slot 3",
			DeviceClass:  "Network Controller",
			PartNumber:   "MCX75310AAS-NEAT",
			BDF:          "0000:af:00.0",
			VendorID:     0x15b3,
			DeviceID:     0x1021,
			SerialNumber: "MT000123",
		},
	}

	out := dedupePCIeDevices(in)
	if len(out) != 1 {
		t.Fatalf("expected merged single PCIe row, got %d", len(out))
	}
	if out[0].BDF != "0000:af:00.0" || out[0].SerialNumber != "MT000123" || out[0].VendorID == 0 || out[0].DeviceID == 0 {
		t.Fatalf("expected richer PCIe fields preserved, got %+v", out[0])
	}
}

func TestAppendPSU_MergesRicherDuplicate(t *testing.T) {
	var out []models.PSU
	seen := make(map[string]int)
	idx := 1
	idx = appendPSU(&out, seen, models.PSU{
		Slot:    "PSU1",
		Model:   "N/A",
		Present: true,
	}, idx)
	_ = appendPSU(&out, seen, models.PSU{
		Slot:         "PSU1",
		Model:        "DLG2700BW54C31",
		SerialNumber: "DGPLV2515025L",
		WattageW:     2700,
		Firmware:     "00.01.04",
		Present:      true,
	}, idx)

	if len(out) != 1 {
		t.Fatalf("expected PSU duplicate merge, got %d rows", len(out))
	}
	if out[0].SerialNumber != "DGPLV2515025L" || out[0].WattageW != 2700 || out[0].Model != "DLG2700BW54C31" {
		t.Fatalf("expected richer PSU fields preserved, got %+v", out[0])
	}
}

func TestRedfishPSUNominalWattage_PrefersInputRangeOutputWattage(t *testing.T) {
	doc := map[string]interface{}{
		"PowerCapacityWatts": 22600,
		"InputRanges": []interface{}{
			map[string]interface{}{"OutputWattage": 2700},
			map[string]interface{}{"OutputWattage": 3200},
		},
	}

	if got := redfishPSUNominalWattage(doc); got != 3200 {
		t.Fatalf("redfishPSUNominalWattage() = %d, want 3200", got)
	}
}

func TestReplayCollectGPUs_DropsModelOnlyPlaceholderWhenConcreteDiscoveredLater(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU0"},
			},
		},
		"/redfish/v1/Systems/1/GraphicsControllers/GPU0": map[string]interface{}{
			"Id":    "GPU0",
			"Name":  "H200-SXM5-141G",
			"Model": "H200-SXM5-141G",
		},
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/4"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/4": map[string]interface{}{
			"Id":           "4",
			"Name":         "PCIeCard4",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"BDF":          "0000:0f:00.0",
		},
	}}

	got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, []string{"/redfish/v1/Chassis/1"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	if len(got) != 1 {
		t.Fatalf("expected generic graphics placeholder to be dropped, got %d GPUs", len(got))
	}
	if got[0].Slot != "PCIeCard4" {
		t.Fatalf("expected concrete PCIe GPU to remain, got slot=%q", got[0].Slot)
	}
}

func TestReplayCollectGPUs_MergesGraphicsSerialIntoConcretePCIeGPU(t *testing.T) {
	r := redfishSnapshotReader{tree: map[string]interface{}{
		"/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU4"},
			},
		},
		"/redfish/v1/Systems/1/GraphicsControllers/GPU4": map[string]interface{}{
			"Id":           "4",
			"Name":         "H100-PCIE-80G",
			"Model":        "H100-PCIE-80G",
			"Manufacturer": "NVIDIA",
			"Oem": map[string]interface{}{
				"SerialNumber": "1794024010533",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/8"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/8": map[string]interface{}{
			"Id":           "8",
			"Name":         "PCIeCard8",
			"Model":        "H100-PCIE-80G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "N/A",
			"BDF":          "0000:b1:00.0",
		},
	}}

	got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, []string{"/redfish/v1/Chassis/1"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	if len(got) != 1 {
		t.Fatalf("expected merged single GPU row, got %d", len(got))
	}
	if got[0].Slot != "PCIeCard8" {
		t.Fatalf("expected concrete PCIe slot, got %q", got[0].Slot)
	}
	if got[0].SerialNumber != "1794024010533" {
		t.Fatalf("expected merged serial from graphics controller, got %q", got[0].SerialNumber)
	}
}

func TestReplayCollectGPUs_MergesAmbiguousSameModelByOrder(t *testing.T) {
	tree := map[string]interface{}{
		"/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{
			"Members": []interface{}{},
		},
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{},
		},
	}

	pcieIDs := []int{4, 8, 12, 14, 20, 23, 26, 30}
	serials := []string{
		"1654425002361",
		"1654425004310",
		"1654425004204",
		"1654225097289",
		"1654225095717",
		"1654425002114",
		"1654425002714",
		"1654425002991",
	}
	for i := 0; i < len(pcieIDs); i++ {
		gpuPath := fmt.Sprintf("/redfish/v1/Systems/1/GraphicsControllers/GPU%d", i+1)
		pciePath := fmt.Sprintf("/redfish/v1/Chassis/1/PCIeDevices/%d", pcieIDs[i])
		tree["/redfish/v1/Systems/1/GraphicsControllers"].(map[string]interface{})["Members"] =
			append(tree["/redfish/v1/Systems/1/GraphicsControllers"].(map[string]interface{})["Members"].([]interface{}), map[string]interface{}{"@odata.id": gpuPath})
		tree["/redfish/v1/Chassis/1/PCIeDevices"].(map[string]interface{})["Members"] =
			append(tree["/redfish/v1/Chassis/1/PCIeDevices"].(map[string]interface{})["Members"].([]interface{}), map[string]interface{}{"@odata.id": pciePath})

		tree[gpuPath] = map[string]interface{}{
			"Id":           fmt.Sprintf("GPU%d", i+1),
			"Name":         "H200-SXM5-141G",
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"SerialNumber": serials[i],
		}
		tree[pciePath] = map[string]interface{}{
			"Id":           fmt.Sprintf("%d", pcieIDs[i]),
			"Name":         fmt.Sprintf("PCIeCard%d", pcieIDs[i]),
			"Model":        "H200-SXM5-141G",
			"Manufacturer": "NVIDIA",
			"BDF":          fmt.Sprintf("0000:%02x:00.0", i+1),
		}
	}

	r := redfishSnapshotReader{tree: tree}
	got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, []string{"/redfish/v1/Chassis/1"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	if len(got) != len(pcieIDs) {
		t.Fatalf("expected %d merged GPUs, got %d", len(pcieIDs), len(got))
	}

	bySlot := make(map[string]models.GPU, len(got))
	for _, gpu := range got {
		bySlot[gpu.Slot] = gpu
		if strings.EqualFold(strings.TrimSpace(gpu.Slot), strings.TrimSpace(gpu.Model)) {
			t.Fatalf("expected model-only placeholder to be dropped, got slot=%q", gpu.Slot)
		}
	}
	for i, id := range pcieIDs {
		slot := fmt.Sprintf("PCIeCard%d", id)
		gpu, ok := bySlot[slot]
		if !ok {
			t.Fatalf("expected concrete slot %q in output", slot)
		}
		if gpu.SerialNumber != serials[i] {
			t.Fatalf("expected slot %s serial %s, got %s", slot, serials[i], gpu.SerialNumber)
		}
	}
}

// TestCollectGPUsFromProcessors_SupermicroHGX verifies that GPU-type Processor
// entries (Supermicro HGX: HGX_Baseboard_0/Processors/GPU_SXM_N) are not added
// as duplicates when the same GPU is already present via Chassis PCIeDevices.
// The processor doc carries SerialNumber directly; the chassis ID ("HGX_GPU_SXM_1")
// does NOT match the processor Id ("GPU_SXM_1"), so chassis-based serial lookup
// fails and the dedup must fall back to the processor doc's own SerialNumber.
func TestCollectGPUsFromProcessors_SupermicroHGX(t *testing.T) {
	tree := map[string]interface{}{
		// Main chassis PCIeDevices — GPU1 and GPU2 with serials.
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU2"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU1": map[string]interface{}{
			"Id":              "GPU1",
			"Name":            "GPU1",
			"Model":           "NVIDIA H200",
			"Manufacturer":    "NVIDIA",
			"SerialNumber":    "SN001",
			"FirmwareVersion": "96.00.D9.00.02",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1": map[string]interface{}{
			"FunctionId": "1",
			"ClassCode":  "0x030200",
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU2": map[string]interface{}{
			"Id":           "GPU2",
			"Name":         "GPU2",
			"Model":        "NVIDIA H200",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "SN002",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions/1": map[string]interface{}{
			"FunctionId": "2",
			"ClassCode":  "0x030200",
		},
		// HGX GPU chassis — named HGX_GPU_SXM_N (NOT GPU_SXM_N), so chassis-ID lookup
		// by processor Id "GPU_SXM_1" will NOT find them.
		"/redfish/v1/Chassis/HGX_GPU_SXM_1": map[string]interface{}{
			"Id": "HGX_GPU_SXM_1",
		},
		"/redfish/v1/Chassis/HGX_GPU_SXM_2": map[string]interface{}{
			"Id": "HGX_GPU_SXM_2",
		},
		// HGX Baseboard system with GPU-type Processors carrying the same serials.
		"/redfish/v1/Systems/HGX_Baseboard_0/Processors": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1"},
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_2"},
			},
		},
		"/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1": map[string]interface{}{
			"Id":            "GPU_SXM_1",
			"Name":          "Processor",
			"ProcessorType": "GPU",
			"Model":         "NVIDIA H200",
			"Manufacturer":  "NVIDIA",
			"SerialNumber":  "SN001",
			"UUID":          "aaaaaaaa-0000-0000-0000-000000000001",
			"Location": map[string]interface{}{
				"PartLocation": map[string]interface{}{
					"ServiceLabel": "SXM1",
				},
			},
		},
		"/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_2": map[string]interface{}{
			"Id":            "GPU_SXM_2",
			"Name":          "Processor",
			"ProcessorType": "GPU",
			"Model":         "NVIDIA H200",
			"Manufacturer":  "NVIDIA",
			"SerialNumber":  "SN002",
			"UUID":          "aaaaaaaa-0000-0000-0000-000000000002",
			"Location": map[string]interface{}{
				"PartLocation": map[string]interface{}{
					"ServiceLabel": "SXM2",
				},
			},
		},
	}

	r := redfishSnapshotReader{tree: tree}
	chassisPaths := []string{
		"/redfish/v1/Chassis/1",
		"/redfish/v1/Chassis/HGX_GPU_SXM_1",
		"/redfish/v1/Chassis/HGX_GPU_SXM_2",
	}
	systemPaths := []string{"/redfish/v1/Systems/HGX_Baseboard_0"}

	gpus := r.collectGPUs(systemPaths, chassisPaths, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	gpus = r.collectGPUsFromProcessors(systemPaths, chassisPaths, gpus, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableProcessorGPUFallback: true}))

	if len(gpus) != 2 {
		var slots []string
		for _, g := range gpus {
			slots = append(slots, fmt.Sprintf("%s(sn=%s)", g.Slot, g.SerialNumber))
		}
		t.Fatalf("expected 2 GPUs (no duplicates), got %d: %v", len(gpus), slots)
	}
}

func TestCollectGPUsFromProcessors_SupermicroHGXUsesChassisAliasSerial(t *testing.T) {
	tree := map[string]interface{}{
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU1": map[string]interface{}{
			"Id":           "GPU1",
			"Name":         "GPU1",
			"Model":        "NVIDIA H200",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "SN-ALIAS-001",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1": map[string]interface{}{
			"FunctionId": "1",
			"ClassCode":  "0x030200",
		},
		"/redfish/v1/Chassis/HGX_GPU_SXM_1": map[string]interface{}{
			"Id":           "HGX_GPU_SXM_1",
			"SerialNumber": "SN-ALIAS-001",
		},
		"/redfish/v1/Systems/HGX_Baseboard_0/Processors": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1"},
			},
		},
		"/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1": map[string]interface{}{
			"Id":            "GPU_SXM_1",
			"Name":          "Processor",
			"ProcessorType": "GPU",
			"Model":         "NVIDIA H200",
			"Manufacturer":  "NVIDIA",
		},
	}

	r := redfishSnapshotReader{tree: tree}
	chassisPaths := []string{
		"/redfish/v1/Chassis/1",
		"/redfish/v1/Chassis/HGX_GPU_SXM_1",
	}
	systemPaths := []string{"/redfish/v1/Systems/HGX_Baseboard_0"}

	gpus := r.collectGPUs(systemPaths, chassisPaths, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	gpus = r.collectGPUsFromProcessors(systemPaths, chassisPaths, gpus, redfishprofile.ResolvedAnalysisPlan{
		Directives:                     redfishprofile.AnalysisDirectives{EnableProcessorGPUFallback: true, EnableProcessorGPUChassisAlias: true},
		ProcessorGPUChassisLookupModes: []string{"hgx-alias"},
	})

	if len(gpus) != 1 {
		t.Fatalf("expected alias serial dedupe to keep 1 gpu, got %d", len(gpus))
	}
	if gpus[0].SerialNumber != "SN-ALIAS-001" {
		t.Fatalf("expected serial from aliased chassis, got %q", gpus[0].SerialNumber)
	}
}

func TestCollectGPUsFromProcessors_MSIUsesIndexedChassisLookup(t *testing.T) {
	tree := map[string]interface{}{
		"/redfish/v1/Chassis/GPU1": map[string]interface{}{
			"Id":           "GPU1",
			"SerialNumber": "MSI-SN-001",
		},
		"/redfish/v1/Systems/1/Processors": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Processors/GPU_SXM_1"},
			},
		},
		"/redfish/v1/Systems/1/Processors/GPU_SXM_1": map[string]interface{}{
			"Id":            "GPU_SXM_1",
			"Name":          "Processor",
			"ProcessorType": "GPU",
			"Model":         "NVIDIA RTX PRO 6000 Blackwell",
			"Manufacturer":  "NVIDIA",
		},
	}

	r := redfishSnapshotReader{tree: tree}
	gpus := r.collectGPUsFromProcessors(
		[]string{"/redfish/v1/Systems/1"},
		[]string{"/redfish/v1/Chassis/GPU1"},
		nil,
		redfishprofile.ResolvedAnalysisPlan{
			Directives:                     redfishprofile.AnalysisDirectives{EnableProcessorGPUFallback: true, EnableMSIProcessorGPUChassisLookup: true},
			ProcessorGPUChassisLookupModes: []string{"msi-index"},
		},
	)

	if len(gpus) != 1 {
		t.Fatalf("expected one gpu, got %d", len(gpus))
	}
	if gpus[0].SerialNumber != "MSI-SN-001" {
		t.Fatalf("expected serial from MSI indexed chassis lookup, got %q", gpus[0].SerialNumber)
	}
}

// TestReplayCollectGPUs_DedupCrossChassisSerial verifies that the same GPU
// appearing under two Chassis PCIeDevice trees (e.g. Chassis/1/PCIeDevices/GPU1
// and Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1) is deduplicated to one entry
// when both expose the same serial number.
func TestReplayCollectGPUs_DedupCrossChassisSerial(t *testing.T) {
	tree := map[string]interface{}{
		"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU1": map[string]interface{}{
			"@odata.id":    "/redfish/v1/Chassis/1/PCIeDevices/GPU1",
			"Id":           "GPU1",
			"Name":         "GPU1",
			"Model":        "NVIDIA H200",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "SN-CROSSTEST-001",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1"},
			},
		},
		"/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1": map[string]interface{}{
			"FunctionId": "1",
			"ClassCode":  "0x030200",
		},
		// Same GPU exposed via dedicated HGX chassis — same serial, different path.
		"/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1"},
			},
		},
		"/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1": map[string]interface{}{
			"@odata.id":    "/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1",
			"Id":           "GPU_SXM_1",
			"Name":         "PCIe Device",
			"Model":        "NVIDIA H200",
			"Manufacturer": "NVIDIA",
			"SerialNumber": "SN-CROSSTEST-001",
			"UUID":         "deadbeef-0000-0000-0000-000000000001",
			"PCIeFunctions": map[string]interface{}{
				"@odata.id": "/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1/PCIeFunctions",
			},
		},
		"/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1/PCIeFunctions": map[string]interface{}{
			"Members": []interface{}{},
		},
	}

	r := redfishSnapshotReader{tree: tree}
	got := r.collectGPUs(nil, []string{
		"/redfish/v1/Chassis/1",
		"/redfish/v1/Chassis/HGX_GPU_SXM_1",
	}, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true}))
	if len(got) != 1 {
		var slots []string
		for _, g := range got {
			slots = append(slots, fmt.Sprintf("%s(sn=%s)", g.Slot, g.SerialNumber))
		}
		t.Fatalf("expected 1 GPU (cross-chassis serial dedup), got %d: %v", len(got), slots)
	}
	if got[0].SerialNumber != "SN-CROSSTEST-001" {
		t.Fatalf("unexpected serial %q", got[0].SerialNumber)
	}
}

// TestLooksLikeGPU_NVSwitchExcluded verifies that NVSwitch PCIe devices
// are not classified as GPUs even though their manufacturer is NVIDIA.
func TestLooksLikeGPU_NVSwitchExcluded(t *testing.T) {
	doc := map[string]interface{}{
		"Id":           "NVSwitch_0",
		"Name":         "PCIe Device",
		"Model":        "NVSwitch",
		"Manufacturer": "NVIDIA",
		"DeviceType":   "SingleFunction",
	}
	if looksLikeGPU(doc, nil) {
		t.Fatal("NVSwitch should not be classified as a GPU")
	}
}

func TestFirmwareInventoryDeviceName_PrefersIDForGenericSoftwareInventory(t *testing.T) {
	doc := map[string]interface{}{
		"Id":      "HGX_FW_NVSwitch_0",
		"Name":    "Software Inventory",
		"Version": "96.10.73.00.01",
	}

	got := firmwareInventoryDeviceName(doc)
	if got != "HGX_FW_NVSwitch_0" {
		t.Fatalf("expected firmware inventory id to be used, got %q", got)
	}
}

func TestParsePCIeDeviceWithSupplementalDocs_NVSwitchThermalMetrics(t *testing.T) {
	doc := map[string]interface{}{
		"@odata.id":    "/redfish/v1/Chassis/HGX_NVSwitch_0/PCIeDevices/NVSwitch_0",
		"Id":           "NVSwitch_0",
		"Model":        "NVSwitch",
		"Manufacturer": "NVIDIA",
		"DeviceType":   "SingleFunction",
	}

	supplementalDocs := []map[string]interface{}{
		{
			"TemperatureReadingsCelsius": []interface{}{
				map[string]interface{}{
					"DeviceName": "NVSwitch_0",
					"Reading":    "31.593750",
				},
			},
		},
	}

	got := parsePCIeDeviceWithSupplementalDocs(doc, nil, supplementalDocs)
	if got.Details == nil {
		t.Fatalf("expected NVSwitch details to be populated")
	}
	if temp := got.Details["temperature_c"]; temp != 31.59375 {
		t.Fatalf("expected NVSwitch thermal metric, got %#v", got.Details)
	}
}

func TestShouldCrawlPath_MemoryAndProcessorMetricsAreAllowed(t *testing.T) {
	if !shouldCrawlPath("/redfish/v1/Systems/1/Memory/CPU0_C0D0") {
		t.Fatalf("expected direct DIMM resource to be crawlable")
	}
	if shouldCrawlPath("/redfish/v1/Systems/1/Memory/CPU0_C0D0/Assembly") {
		t.Fatalf("expected DIMM assembly subresource to be skipped")
	}
	if !shouldCrawlPath("/redfish/v1/Systems/1/Memory/CPU0_C0D0/MemoryMetrics") {
		t.Fatalf("expected DIMM metrics subresource to be crawlable")
	}
	if !shouldCrawlPath("/redfish/v1/Systems/1/Processors/CPU0/ProcessorMetrics") {
		t.Fatalf("expected CPU metrics subresource to be crawlable")
	}
	if shouldCrawlPath("/redfish/v1/Chassis/1/PCIeDevices/0/PCIeFunctions") {
		t.Fatalf("expected broad chassis PCIeFunctions collection to be skipped")
	}
	if !shouldCrawlPath("/redfish/v1/Chassis/1/PCIeDevices/0/PCIeFunctions/1") {
		t.Fatalf("expected direct chassis PCIeFunction member to remain crawlable")
	}
	if !shouldCrawlPath("/redfish/v1/Fabrics/HGX_NVLinkFabric_0/Switches/NVSwitch_0") {
		t.Fatalf("expected NVSwitch fabric resource to be crawlable")
	}
	if !shouldCrawlPath("/redfish/v1/Fabrics/HGX_NVLinkFabric_0/Switches/NVSwitch_0/Ports/NVLink_0/Metrics") {
		t.Fatalf("expected NVLink port metrics to be crawlable")
	}
}

func TestIsRedfishMemoryMemberPath(t *testing.T) {
	cases := []struct {
		path string
		want bool
	}{
		{path: "/redfish/v1/Systems/1/Memory", want: false},
		{path: "/redfish/v1/Systems/1/Memory/CPU0_C0D0", want: true},
		{path: "/redfish/v1/Systems/1/Memory/CPU0_C0D0/Assembly", want: false},
		{path: "/redfish/v1/Systems/1/Memory/CPU0_C0D0/MemoryMetrics", want: false},
		{path: "/redfish/v1/Chassis/1/Memory/CPU0_C0D0", want: false},
	}
	for _, tc := range cases {
		got := isRedfishMemoryMemberPath(tc.path)
		if got != tc.want {
			t.Fatalf("isRedfishMemoryMemberPath(%q) = %v, want %v", tc.path, got, tc.want)
		}
	}
}

func TestRedfishSnapshotBranchKey(t *testing.T) {
	cases := []struct {
		path string
		want string
	}{
		{path: "", want: ""},
		{path: "/redfish/v1", want: ""},
		{path: "/redfish/v1/Systems", want: "/redfish/v1/Systems"},
		{path: "/redfish/v1/Systems/1", want: "/redfish/v1/Systems/1"},
		{path: "/redfish/v1/Systems/1/Memory", want: "/redfish/v1/Systems/1/Memory"},
		{path: "/redfish/v1/Systems/1/Memory/CPU0_C0D0", want: "/redfish/v1/Systems/1/Memory"},
		{path: "/redfish/v1/Systems/1/PCIeDevices/GPU1", want: "/redfish/v1/Systems/1/PCIeDevices"},
		{path: "/redfish/v1/Chassis/1/Sensors/1", want: "/redfish/v1/Chassis/1/Sensors"},
		{path: "/redfish/v1/UpdateService/FirmwareInventory/BIOS", want: "/redfish/v1/UpdateService/FirmwareInventory"},
	}
	for _, tc := range cases {
		got := redfishSnapshotBranchKey(tc.path)
		if got != tc.want {
			t.Fatalf("redfishSnapshotBranchKey(%q) = %q, want %q", tc.path, got, tc.want)
		}
	}
}

func TestShouldPostProbeCollectionPath(t *testing.T) {
	var tuning redfishprofile.AcquisitionTuning
	if shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Sensors", tuning) {
		t.Fatalf("expected sensors collection to be skipped by default")
	}
	if shouldPostProbeCollectionPath("/redfish/v1/Systems/1/Storage/RAID/Drives", tuning) {
		t.Fatalf("expected drives collection to be skipped without profile policy")
	}
	tuning.PostProbePolicy.EnableNumericCollectionProbe = true
	t.Setenv("LOGPILE_REDFISH_SENSOR_POSTPROBE", "1")
	if !shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Sensors", tuning) {
		t.Fatalf("expected sensors collection to be post-probed when enabled")
	}
	if !shouldPostProbeCollectionPath("/redfish/v1/Systems/1/Storage/RAID/Drives", tuning) {
		t.Fatalf("expected drives collection to be post-probed")
	}
	if shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Boards/BOARD1", tuning) {
		t.Fatalf("expected board member resource to be skipped from post-probe")
	}
	if shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Assembly/Oem/COMMONb/COMMONbAssembly/1", tuning) {
		t.Fatalf("expected assembly member resource to be skipped from post-probe")
	}
}

func TestShouldAdaptivePostProbeCollectionPath(t *testing.T) {
	tuning := redfishprofile.AcquisitionTuning{
		PostProbePolicy: redfishprofile.AcquisitionPostProbePolicy{
			EnableNumericCollectionProbe: true,
		},
	}
	withExplicitNamedMembers := map[string]interface{}{
		"Members": []interface{}{
			map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/EthernetInterfaces/NIC-0-0"},
			map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/EthernetInterfaces/NIC-0-1"},
		},
	}
	if shouldAdaptivePostProbeCollectionPath("/redfish/v1/Systems/1/EthernetInterfaces", withExplicitNamedMembers, tuning) {
		t.Fatalf("expected explicit non-numeric members to skip adaptive post-probe")
	}

	withNumericMembers := map[string]interface{}{
		"Members": []interface{}{
			map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/1"},
			map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/2"},
		},
	}
	if !shouldAdaptivePostProbeCollectionPath("/redfish/v1/Chassis/1/PCIeDevices", withNumericMembers, tuning) {
		t.Fatalf("expected numeric members to allow adaptive post-probe")
	}

	withoutMembers := map[string]interface{}{"Name": "Drives"}
	if !shouldAdaptivePostProbeCollectionPath("/redfish/v1/Chassis/1/Drives", withoutMembers, tuning) {
		t.Fatalf("expected missing members to allow adaptive post-probe")
	}

	if shouldAdaptivePostProbeCollectionPath("/redfish/v1/Chassis/1/Drives", withoutMembers, redfishprofile.AcquisitionTuning{}) {
		t.Fatalf("expected post-probe to stay disabled without profile policy")
	}
}

func TestShouldAdaptiveNVMeProbe(t *testing.T) {
	withMembers := map[string]interface{}{
		"Members": []interface{}{
			map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/OB01"},
		},
	}
	if shouldAdaptiveNVMeProbe(withMembers) {
		t.Fatalf("expected drives collection with explicit members to skip NVMe probe")
	}

	withoutMembers := map[string]interface{}{"Name": "Drives"}
	if !shouldAdaptiveNVMeProbe(withoutMembers) {
		t.Fatalf("expected drives collection without members to allow NVMe probe")
	}
}

func TestRedfishAdaptivePrefetchTargets(t *testing.T) {
	tuning := redfishprofile.AcquisitionTuning{
		PrefetchPolicy: redfishprofile.AcquisitionPrefetchPolicy{
			IncludeSuffixes: []string{
				"/Memory",
				"/Processors",
				"/Storage",
			},
		},
	}
	candidates := []string{
		"/redfish/v1/Systems/1/Memory",
		"/redfish/v1/Systems/1/Processors",
		"/redfish/v1/Systems/1/Storage",
	}
	rawTree := map[string]interface{}{
		"/redfish/v1/Systems/1/Memory": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Memory/DIMM1"},
			},
		},
		"/redfish/v1/Systems/1/Storage": map[string]interface{}{
			"Members": []interface{}{
				map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/1"},
			},
		},
	}
	fetchErrs := map[string]string{
		"/redfish/v1/Systems/1/Memory/DIMM1":    "Get \"https://bmc/redfish/v1/Systems/1/Memory/DIMM1\": context deadline exceeded",
		"/redfish/v1/Systems/1/Storage/1":       "status 404 from /redfish/v1/Systems/1/Storage/1: not found",
		"/redfish/v1/Systems/1/Processors":      "Get \"https://bmc/redfish/v1/Systems/1/Processors\": context deadline exceeded",
		"/redfish/v1/Systems/1/Storage/Volumes": "status 404 from /redfish/v1/Systems/1/Storage/Volumes: not found",
	}

	got := redfishAdaptivePrefetchTargets(redfishPrefetchTargets(candidates, tuning), rawTree, fetchErrs)
	joined := strings.Join(got, "\n")
	for _, wanted := range []string{
		"/redfish/v1/Systems/1/Memory",
		"/redfish/v1/Systems/1/Processors",
	} {
		if !strings.Contains(joined, wanted) {
			t.Fatalf("expected adaptive prefetch target %q", wanted)
		}
	}
	if strings.Contains(joined, "/redfish/v1/Systems/1/Storage") {
		t.Fatalf("expected storage with only non-retryable missing members to be skipped")
	}
}

func TestResolveAcquisitionPlan_DefaultSkipsNoisyBranches(t *testing.T) {
	signals := redfishprofile.MatchSignals{}
	match := redfishprofile.MatchProfiles(signals)
	plan := redfishprofile.BuildAcquisitionPlan(signals)
	resolved := redfishprofile.ResolveAcquisitionPlan(match, plan, redfishprofile.DiscoveredResources{
		SystemPaths:  []string{"/redfish/v1/Systems/1"},
		ChassisPaths: []string{"/redfish/v1/Chassis/1"},
		ManagerPaths: []string{"/redfish/v1/Managers/1"},
	}, signals)
	seeds := resolved.SeedPaths
	joined := strings.Join(seeds, "\n")
	for _, noisy := range []string{
		"/redfish/v1/Fabrics",
		"/redfish/v1/Chassis/1/Backplanes",
		"/redfish/v1/Chassis/1/Boards",
		"/redfish/v1/Chassis/1/Sensors",
		"/redfish/v1/Managers/1/LogServices",
	} {
		if strings.Contains(joined, noisy) {
			t.Fatalf("unexpected noisy seed %q", noisy)
		}
	}
	for _, wanted := range []string{
		"/redfish/v1/Systems/1/Memory",
		"/redfish/v1/Systems/1/PCIeDevices",
		"/redfish/v1/Chassis/1/Drives",
		"/redfish/v1/Chassis/1/NetworkAdapters",
		"/redfish/v1/Managers/1/NetworkProtocol",
	} {
		if !strings.Contains(joined, wanted) {
			t.Fatalf("expected seed %q", wanted)
		}
	}
}

func TestShouldPrefetchCriticalPath_UsesPrefetchPolicy(t *testing.T) {
	tuning := redfishprofile.AcquisitionTuning{
		PrefetchPolicy: redfishprofile.AcquisitionPrefetchPolicy{
			IncludeSuffixes: []string{"/Storage", "/Oem/Public"},
			ExcludeContains: []string{"/Assembly"},
		},
	}
	if !shouldPrefetchCriticalPath("/redfish/v1/Systems/1/Storage", tuning) {
		t.Fatal("expected storage path to be prefetched when included by policy")
	}
	if !shouldPrefetchCriticalPath("/redfish/v1/Systems/1/Oem/Public", tuning) {
		t.Fatal("expected OEM public path to be prefetched when included by policy")
	}
	if shouldPrefetchCriticalPath("/redfish/v1/Chassis/1/Assembly", tuning) {
		t.Fatal("expected excluded path to skip prefetch")
	}
	if shouldPrefetchCriticalPath("/redfish/v1/Chassis/1/Power", redfishprofile.AcquisitionTuning{}) {
		t.Fatal("expected empty prefetch policy to disable suffix-based prefetch")
	}
}

func TestRedfishPrefetchTargets_FilterNoisyBranches(t *testing.T) {
	tuning := redfishprofile.AcquisitionTuning{
		PrefetchPolicy: redfishprofile.AcquisitionPrefetchPolicy{
			IncludeSuffixes: []string{
				"/Memory",
				"/Oem/Public/FRU",
				"/Drives",
				"/NetworkProtocol",
			},
			ExcludeContains: []string{
				"/Backplanes",
				"/Sensors",
				"/LogServices",
			},
		},
	}
	critical := []string{
		"/redfish/v1/Systems/1",
		"/redfish/v1/Systems/1/Memory",
		"/redfish/v1/Systems/1/Oem/Public/FRU",
		"/redfish/v1/Chassis/1/Drives",
		"/redfish/v1/Chassis/1/Backplanes",
		"/redfish/v1/Chassis/1/Sensors",
		"/redfish/v1/Managers/1/LogServices",
		"/redfish/v1/Managers/1/NetworkProtocol",
	}
	got := redfishPrefetchTargets(critical, tuning)
	joined := strings.Join(got, "\n")
	for _, wanted := range []string{
		"/redfish/v1/Systems/1",
		"/redfish/v1/Systems/1/Memory",
		"/redfish/v1/Systems/1/Oem/Public/FRU",
		"/redfish/v1/Chassis/1/Drives",
		"/redfish/v1/Managers/1/NetworkProtocol",
	} {
		if !strings.Contains(joined, wanted) {
			t.Fatalf("expected prefetch target %q", wanted)
		}
	}
	for _, noisy := range []string{
		"/redfish/v1/Chassis/1/Backplanes",
		"/redfish/v1/Chassis/1/Sensors",
		"/redfish/v1/Managers/1/LogServices",
	} {
		if strings.Contains(joined, noisy) {
			t.Fatalf("unexpected noisy prefetch target %q", noisy)
		}
	}
}

// TestChassisTypeCanHaveNVMe verifies that non-storage chassis types (GPU modules,
// RoT components, fabric zones) are excluded from NVMe bay probing, while storage
// and unclassified chassis types are kept.
//
// Regression guard: on Supermicro HGX (SYS-A21GE-NBRT) all 35 sub-chassis (GPUs,
// NVSwitches, PCIeRetimers, ERoT/IRoT, BMC, FPGA) have ChassisType=Module/Component/Zone
// and expose empty /Drives collections. Without this filter each chassis triggered
// 384 HTTP requests → ~22 minutes wasted per collection. (2026-03-12)
func TestChassisTypeCanHaveNVMe(t *testing.T) {
	cases := []struct {
		chassisType string
		want        bool
	}{
		// Non-storage sub-module types — must return false
		{"Module", false},    // GPU SXM, PCIeRetimer, NVLinkManagementNIC
		{"module", false},    // case-insensitive
		{"Component", false}, // ERoT, IRoT, BMC, FPGA sub-chassis
		{"component", false},
		{"Zone", false}, // HGX_Chassis_0 fabric zone
		{"zone", false},
		// Storage-capable and generic types — must return true
		{"Enclosure", true},  // NVMe StorageBackplane
		{"RackMount", true},  // main server chassis
		{"Blade", true},      // blade server chassis
		{"StandAlone", true}, // standalone server
		{"", true},           // unknown type — probe to be safe
	}
	for _, tc := range cases {
		got := chassisTypeCanHaveNVMe(tc.chassisType)
		if got != tc.want {
			t.Errorf("chassisTypeCanHaveNVMe(%q) = %v, want %v", tc.chassisType, got, tc.want)
		}
	}
}

// TestNVMePostProbeSkipsNonStorageChassis verifies that the NVMe bay probe candidate
// selection skips chassis whose ChassisType indicates they cannot hold NVMe drives.
//
// Simulates an HGX topology: one GPU chassis (Module) and one NVMe backplane
// (Enclosure), both with empty /Drives collections. Only the backplane must be
// selected as a probe candidate.
func TestNVMePostProbeSkipsNonStorageChassis(t *testing.T) {
	// Build the out map as collectRawRedfishTree would produce it
	out := map[string]interface{}{
		// GPU chassis — Module type, empty Drives: should be skipped
		"/redfish/v1/Chassis/HGX_GPU_SXM_1": map[string]interface{}{
			"@odata.id":   "/redfish/v1/Chassis/HGX_GPU_SXM_1",
			"ChassisType": "Module",
			"Name":        "HGX_GPU_SXM_1",
		},
		"/redfish/v1/Chassis/HGX_GPU_SXM_1/Drives": map[string]interface{}{
			"@odata.id":           "/redfish/v1/Chassis/HGX_GPU_SXM_1/Drives",
			"Members":             []interface{}{},
			"Members@odata.count": 0,
		},
		// NVMe backplane — Enclosure type, empty Drives: must be selected
		"/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane": map[string]interface{}{
			"@odata.id":   "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane",
			"ChassisType": "Enclosure",
			"Name":        "Backplane",
		},
		"/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives": map[string]interface{}{
			"@odata.id":           "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives",
			"Members":             []interface{}{},
			"Members@odata.count": 0,
		},
	}

	// Replicate the candidate selection logic from collectRawRedfishTree
	var selected []string
	for path, docAny := range out {
		normalized := normalizeRedfishPath(path)
		if !strings.HasSuffix(normalized, "/Drives") {
			continue
		}
		doc, _ := docAny.(map[string]interface{})
		if !shouldAdaptiveNVMeProbe(doc) {
			continue
		}
		chassisPath := strings.TrimSuffix(normalized, "/Drives")
		if chassisDocAny, ok := out[chassisPath]; ok {
			if chassisDoc, ok := chassisDocAny.(map[string]interface{}); ok {
				if !chassisTypeCanHaveNVMe(asString(chassisDoc["ChassisType"])) {
					continue
				}
			}
		}
		selected = append(selected, normalized)
	}

	if len(selected) != 1 {
		t.Fatalf("expected 1 NVMe probe candidate (backplane), got %d: %v", len(selected), selected)
	}
	if !strings.Contains(selected[0], "StorageBackplane") {
		t.Fatalf("expected StorageBackplane to be selected, got %q", selected[0])
	}
}

func TestIsVirtualStorageDrive_AMIVirtualMedia(t *testing.T) {
	doc := map[string]interface{}{
		"Id":            "USB_Device1_Port4",
		"Name":          "Virtual Cdrom Device",
		"Model":         "Virtual Cdrom Device",
		"Manufacturer":  "American Megatrends Inc.",
		"Protocol":      "USB",
		"CapacityBytes": 0,
		"SerialNumber":  "AAAABBBBCCCC1",
	}

	if !isVirtualStorageDrive(doc) {
		t.Fatalf("expected AMI virtual media to be filtered")
	}
}
