package redfishprofile

import (
	"strings"
	"testing"
)

func TestMatchProfiles_UnknownVendorFallsBackToAggregateProfiles(t *testing.T) {
	match := MatchProfiles(MatchSignals{
		ServiceRootProduct: "Redfish Server",
	})
	if match.Mode != ModeFallback {
		t.Fatalf("expected fallback mode, got %q", match.Mode)
	}
	if len(match.Profiles) < 2 {
		t.Fatalf("expected aggregated fallback profiles, got %d", len(match.Profiles))
	}
}

func TestMatchProfiles_MSISelectsMatchedMode(t *testing.T) {
	match := MatchProfiles(MatchSignals{
		SystemManufacturer: "Micro-Star International Co., Ltd.",
		ResourceHints:      []string{"/redfish/v1/Chassis/GPU1"},
	})
	if match.Mode != ModeMatched {
		t.Fatalf("expected matched mode, got %q", match.Mode)
	}
	found := false
	for _, profile := range match.Profiles {
		if profile.Name() == "msi" {
			found = true
			break
		}
	}
	if !found {
		t.Fatal("expected msi profile to be selected")
	}
}

func TestBuildAcquisitionPlan_FallbackIncludesProfileNotes(t *testing.T) {
	plan := BuildAcquisitionPlan(MatchSignals{
		ServiceRootVendor: "AMI",
	})
	if len(plan.Profiles) == 0 {
		t.Fatal("expected acquisition plan profiles")
	}
	if len(plan.Notes) == 0 {
		t.Fatal("expected acquisition plan notes")
	}
}

func TestBuildAcquisitionPlan_FallbackAddsBroadCrawlTuning(t *testing.T) {
	plan := BuildAcquisitionPlan(MatchSignals{
		ServiceRootProduct: "Unknown Redfish",
	})
	if plan.Mode != ModeFallback {
		t.Fatalf("expected fallback mode, got %q", plan.Mode)
	}
	if plan.Tuning.SnapshotMaxDocuments < 180000 {
		t.Fatalf("expected widened snapshot cap, got %d", plan.Tuning.SnapshotMaxDocuments)
	}
	if plan.Tuning.PrefetchEnabled == nil || !*plan.Tuning.PrefetchEnabled {
		t.Fatal("expected fallback to force prefetch enabled")
	}
	if !plan.Tuning.RecoveryPolicy.EnableCriticalCollectionMemberRetry {
		t.Fatal("expected fallback to inherit critical member retry recovery")
	}
	if !plan.Tuning.RecoveryPolicy.EnableCriticalSlowProbe {
		t.Fatal("expected fallback to inherit critical slow probe recovery")
	}
}

func TestBuildAcquisitionPlan_HGXDisablesNVMePostProbe(t *testing.T) {
	plan := BuildAcquisitionPlan(MatchSignals{
		SystemModel:   "HGX B200",
		ResourceHints: []string{"/redfish/v1/Systems/HGX_Baseboard_0"},
	})
	if plan.Mode != ModeMatched {
		t.Fatalf("expected matched mode, got %q", plan.Mode)
	}
	if plan.Tuning.NVMePostProbeEnabled == nil || *plan.Tuning.NVMePostProbeEnabled {
		t.Fatal("expected hgx profile to disable NVMe post-probe")
	}
}

func TestResolveAcquisitionPlan_ExpandsScopedPaths(t *testing.T) {
	signals := MatchSignals{}
	match := MatchProfiles(signals)
	plan := BuildAcquisitionPlan(signals)
	resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
		SystemPaths: []string{"/redfish/v1/Systems/1", "/redfish/v1/Systems/2"},
	}, signals)
	joined := joinResolvedPaths(resolved.SeedPaths)
	for _, wanted := range []string{
		"/redfish/v1/Systems/1/SimpleStorage",
		"/redfish/v1/Systems/1/Storage/IntelVROC",
		"/redfish/v1/Systems/2/SimpleStorage",
		"/redfish/v1/Systems/2/Storage/IntelVROC",
	} {
		if !containsJoinedPath(joined, wanted) {
			t.Fatalf("expected resolved seed path %q", wanted)
		}
	}
}

func TestResolveAcquisitionPlan_CriticalBaselineIsShapedByProfiles(t *testing.T) {
	signals := MatchSignals{}
	match := MatchProfiles(signals)
	plan := BuildAcquisitionPlan(signals)
	resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
		SystemPaths:  []string{"/redfish/v1/Systems/1"},
		ChassisPaths: []string{"/redfish/v1/Chassis/1"},
		ManagerPaths: []string{"/redfish/v1/Managers/1"},
	}, signals)
	joined := joinResolvedPaths(resolved.CriticalPaths)
	for _, wanted := range []string{
		"/redfish/v1",
		"/redfish/v1/Systems/1",
		"/redfish/v1/Systems/1/Memory",
		"/redfish/v1/Chassis/1/Assembly",
		"/redfish/v1/Managers/1/NetworkProtocol",
		"/redfish/v1/UpdateService",
	} {
		if !containsJoinedPath(joined, wanted) {
			t.Fatalf("expected resolved critical path %q", wanted)
		}
	}
}

func TestResolveAcquisitionPlan_FallbackAppendsPlanBToSeeds(t *testing.T) {
	signals := MatchSignals{ServiceRootProduct: "Unknown Redfish"}
	match := MatchProfiles(signals)
	plan := BuildAcquisitionPlan(signals)
	if plan.Mode != ModeFallback {
		t.Fatalf("expected fallback mode, got %q", plan.Mode)
	}
	plan.PlanBPaths = append(plan.PlanBPaths, "/redfish/v1/Systems/1/Oem/TestPlanB")
	resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
		SystemPaths: []string{"/redfish/v1/Systems/1"},
	}, signals)
	if !containsJoinedPath(joinResolvedPaths(resolved.SeedPaths), "/redfish/v1/Systems/1/Oem/TestPlanB") {
		t.Fatal("expected fallback resolved seeds to include plan-b path")
	}
}

func TestResolveAcquisitionPlan_MSIRefinesDiscoveredGPUChassis(t *testing.T) {
	signals := MatchSignals{
		SystemManufacturer: "Micro-Star International Co., Ltd.",
		ResourceHints:      []string{"/redfish/v1/Chassis/GPU1", "/redfish/v1/Chassis/GPU4/Sensors"},
	}
	match := MatchProfiles(signals)
	plan := BuildAcquisitionPlan(signals)
	resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
		ChassisPaths: []string{"/redfish/v1/Chassis/1", "/redfish/v1/Chassis/GPU1", "/redfish/v1/Chassis/GPU4"},
	}, signals)
	joinedSeeds := joinResolvedPaths(resolved.SeedPaths)
	joinedCritical := joinResolvedPaths(resolved.CriticalPaths)
	if !containsJoinedPath(joinedSeeds, "/redfish/v1/Chassis/GPU1") || !containsJoinedPath(joinedSeeds, "/redfish/v1/Chassis/GPU4") {
		t.Fatal("expected MSI refinement to add discovered GPU chassis seed paths")
	}
	if containsJoinedPath(joinedSeeds, "/redfish/v1/Chassis/GPU2") {
		t.Fatal("did not expect undiscovered MSI GPU chassis in resolved seeds")
	}
	if !containsJoinedPath(joinedCritical, "/redfish/v1/Chassis/GPU1/Sensors") || !containsJoinedPath(joinedCritical, "/redfish/v1/Chassis/GPU4/Sensors") {
		t.Fatal("expected MSI refinement to add discovered GPU sensor critical paths")
	}
	if containsJoinedPath(joinedCritical, "/redfish/v1/Chassis/GPU3/Sensors") {
		t.Fatal("did not expect undiscovered MSI GPU sensor critical path")
	}
}

func TestResolveAcquisitionPlan_HGXRefinesDiscoveredBaseboardSystems(t *testing.T) {
	signals := MatchSignals{
		SystemManufacturer: "Supermicro",
		SystemModel:        "SYS-821GE-TNHR",
		ChassisModel:       "HGX B200",
		ResourceHints: []string{
			"/redfish/v1/Systems/HGX_Baseboard_0",
			"/redfish/v1/Systems/HGX_Baseboard_0/Processors",
			"/redfish/v1/Systems/1",
		},
	}
	match := MatchProfiles(signals)
	plan := BuildAcquisitionPlan(signals)
	resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
		SystemPaths: []string{"/redfish/v1/Systems/1", "/redfish/v1/Systems/HGX_Baseboard_0"},
	}, signals)
	joinedSeeds := joinResolvedPaths(resolved.SeedPaths)
	joinedCritical := joinResolvedPaths(resolved.CriticalPaths)
	if !containsJoinedPath(joinedSeeds, "/redfish/v1/Systems/HGX_Baseboard_0") || !containsJoinedPath(joinedSeeds, "/redfish/v1/Systems/HGX_Baseboard_0/Processors") {
		t.Fatal("expected HGX refinement to add discovered baseboard system paths")
	}
	if !containsJoinedPath(joinedCritical, "/redfish/v1/Systems/HGX_Baseboard_0") || !containsJoinedPath(joinedCritical, "/redfish/v1/Systems/HGX_Baseboard_0/Processors") {
		t.Fatal("expected HGX refinement to add discovered baseboard critical paths")
	}
	if containsJoinedPath(joinedSeeds, "/redfish/v1/Systems/HGX_Baseboard_1") {
		t.Fatal("did not expect undiscovered HGX baseboard system path")
	}
}

func TestResolveAcquisitionPlan_SupermicroRefinesFirmwareInventoryFromHint(t *testing.T) {
	signals := MatchSignals{
		SystemManufacturer: "Supermicro",
		ResourceHints: []string{
			"/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory",
			"/redfish/v1/Managers/1/Oem/Supermicro/FanMode",
		},
	}
	match := MatchProfiles(signals)
	plan := BuildAcquisitionPlan(signals)
	resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
		ManagerPaths: []string{"/redfish/v1/Managers/1"},
	}, signals)
	joinedCritical := joinResolvedPaths(resolved.CriticalPaths)
	if !containsJoinedPath(joinedCritical, "/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory") {
		t.Fatal("expected Supermicro refinement to add firmware inventory critical path")
	}
	if !containsJoinedPath(joinResolvedPaths(resolved.Plan.PlanBPaths), "/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory") {
		t.Fatal("expected Supermicro refinement to add firmware inventory plan-b path")
	}
}

func TestResolveAcquisitionPlan_DellRefinesDiscoveredIDRACManager(t *testing.T) {
	signals := MatchSignals{
		SystemManufacturer: "Dell Inc.",
		ServiceRootProduct: "iDRAC Redfish Service",
	}
	match := MatchProfiles(signals)
	plan := BuildAcquisitionPlan(signals)
	resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
		ManagerPaths: []string{"/redfish/v1/Managers/1", "/redfish/v1/Managers/iDRAC.Embedded.1"},
	}, signals)
	joinedSeeds := joinResolvedPaths(resolved.SeedPaths)
	joinedCritical := joinResolvedPaths(resolved.CriticalPaths)
	if !containsJoinedPath(joinedSeeds, "/redfish/v1/Managers/iDRAC.Embedded.1") {
		t.Fatal("expected Dell refinement to add discovered iDRAC manager seed path")
	}
	if !containsJoinedPath(joinedCritical, "/redfish/v1/Managers/iDRAC.Embedded.1") {
		t.Fatal("expected Dell refinement to add discovered iDRAC manager critical path")
	}
}

func TestBuildAnalysisDirectives_SupermicroEnablesVendorStorageFallbacks(t *testing.T) {
	signals := MatchSignals{
		SystemManufacturer: "Supermicro",
		SystemModel:        "SYS-821GE",
	}
	match := MatchProfiles(signals)
	plan := ResolveAnalysisPlan(match, map[string]interface{}{
		"/redfish/v1/Chassis/NVMeSSD.1.StorageBackplane/Drives": map[string]interface{}{},
	}, DiscoveredResources{}, signals)
	directives := plan.Directives
	if !directives.EnableSupermicroNVMeBackplane {
		t.Fatal("expected supermicro nvme backplane fallback")
	}
}

func joinResolvedPaths(paths []string) string {
	return "\n" + strings.Join(paths, "\n") + "\n"
}

func containsJoinedPath(joined, want string) bool {
	return strings.Contains(joined, "\n"+want+"\n")
}

func TestBuildAnalysisDirectives_HGXEnablesGPUFallbacks(t *testing.T) {
	signals := MatchSignals{
		SystemManufacturer: "Supermicro",
		SystemModel:        "SYS-821GE-TNHR",
		ChassisModel:       "HGX B200",
		ResourceHints:      []string{"/redfish/v1/Systems/HGX_Baseboard_0", "/redfish/v1/Chassis/HGX_Chassis_0/PCIeDevices/GPU_SXM_1"},
	}
	match := MatchProfiles(signals)
	plan := ResolveAnalysisPlan(match, map[string]interface{}{
		"/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1": map[string]interface{}{"ProcessorType": "GPU"},
		"/redfish/v1/Chassis/HGX_Chassis_0/PCIeDevices/GPU_SXM_1":  map[string]interface{}{},
	}, DiscoveredResources{
		SystemPaths: []string{"/redfish/v1/Systems/HGX_Baseboard_0"},
	}, signals)
	directives := plan.Directives
	if !directives.EnableProcessorGPUFallback {
		t.Fatal("expected processor GPU fallback for hgx profile")
	}
	if !directives.EnableProcessorGPUChassisAlias {
		t.Fatal("expected processor GPU chassis alias resolution for hgx profile")
	}
	if !directives.EnableGenericGraphicsControllerDedup {
		t.Fatal("expected graphics-controller dedup for hgx profile")
	}
}

func TestBuildAnalysisDirectives_MSIEnablesMSIChassisLookup(t *testing.T) {
	signals := MatchSignals{
		SystemManufacturer: "Micro-Star International Co., Ltd.",
	}
	match := MatchProfiles(signals)
	plan := ResolveAnalysisPlan(match, map[string]interface{}{
		"/redfish/v1/Systems/1/Processors/GPU1": map[string]interface{}{"ProcessorType": "GPU"},
		"/redfish/v1/Chassis/GPU1":              map[string]interface{}{},
	}, DiscoveredResources{
		SystemPaths:  []string{"/redfish/v1/Systems/1"},
		ChassisPaths: []string{"/redfish/v1/Chassis/GPU1"},
	}, signals)
	directives := plan.Directives
	if !directives.EnableMSIProcessorGPUChassisLookup {
		t.Fatal("expected MSI processor GPU chassis lookup")
	}
}

func TestBuildAnalysisDirectives_SupermicroEnablesStorageRecovery(t *testing.T) {
	signals := MatchSignals{
		SystemManufacturer: "Supermicro",
	}
	match := MatchProfiles(signals)
	plan := ResolveAnalysisPlan(match, map[string]interface{}{
		"/redfish/v1/Chassis/1/Drives":                   map[string]interface{}{},
		"/redfish/v1/Systems/1/Storage/IntelVROC":        map[string]interface{}{},
		"/redfish/v1/Systems/1/Storage/IntelVROC/Drives": map[string]interface{}{},
	}, DiscoveredResources{}, signals)
	directives := plan.Directives
	if !directives.EnableStorageEnclosureRecovery {
		t.Fatal("expected storage enclosure recovery for supermicro")
	}
	if !directives.EnableKnownStorageControllerRecovery {
		t.Fatal("expected known storage controller recovery for supermicro")
	}
}

func TestMatchProfiles_LenovoXCCSelectsMatchedModeAndExcludesSensors(t *testing.T) {
	match := MatchProfiles(MatchSignals{
		SystemManufacturer:  "Lenovo",
		ChassisManufacturer: "Lenovo",
		OEMNamespaces:       []string{"Lenovo"},
	})
	if match.Mode != ModeMatched {
		t.Fatalf("expected matched mode, got %q", match.Mode)
	}
	found := false
	for _, profile := range match.Profiles {
		if profile.Name() == "lenovo" {
			found = true
			break
		}
	}
	if !found {
		t.Fatal("expected lenovo profile to be selected")
	}

	// Verify the acquisition plan excludes noisy Lenovo-specific snapshot paths.
	plan := BuildAcquisitionPlan(MatchSignals{
		SystemManufacturer:  "Lenovo",
		ChassisManufacturer: "Lenovo",
		OEMNamespaces:       []string{"Lenovo"},
	})
	wantExcluded := []string{"/Sensors/", "/Oem/Lenovo/LEDs/", "/Oem/Lenovo/Slots/"}
	for _, want := range wantExcluded {
		found := false
		for _, ex := range plan.Tuning.SnapshotExcludeContains {
			if ex == want {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("expected SnapshotExcludeContains to include %q, got %v", want, plan.Tuning.SnapshotExcludeContains)
		}
	}
}

func TestMatchProfiles_OrderingIsDeterministic(t *testing.T) {
	signals := MatchSignals{
		SystemManufacturer: "Micro-Star International Co., Ltd.",
		ResourceHints:      []string{"/redfish/v1/Chassis/GPU1"},
	}
	first := MatchProfiles(signals)
	second := MatchProfiles(signals)
	if len(first.Profiles) != len(second.Profiles) {
		t.Fatalf("profile stack size differs across calls: %d vs %d", len(first.Profiles), len(second.Profiles))
	}
	for i := range first.Profiles {
		if first.Profiles[i].Name() != second.Profiles[i].Name() {
			t.Fatalf("profile ordering differs at index %d: %q vs %q", i, first.Profiles[i].Name(), second.Profiles[i].Name())
		}
	}
}

func TestMatchProfiles_FallbackOrderingIsDeterministic(t *testing.T) {
	signals := MatchSignals{ServiceRootProduct: "Unknown Redfish"}
	first := MatchProfiles(signals)
	second := MatchProfiles(signals)
	if first.Mode != ModeFallback || second.Mode != ModeFallback {
		t.Fatalf("expected fallback mode in both calls")
	}
	if len(first.Profiles) != len(second.Profiles) {
		t.Fatalf("fallback profile stack size differs: %d vs %d", len(first.Profiles), len(second.Profiles))
	}
	for i := range first.Profiles {
		if first.Profiles[i].Name() != second.Profiles[i].Name() {
			t.Fatalf("fallback profile ordering differs at index %d: %q vs %q", i, first.Profiles[i].Name(), second.Profiles[i].Name())
		}
	}
}

func TestMatchProfiles_FallbackOnlySelectsSafeProfiles(t *testing.T) {
	match := MatchProfiles(MatchSignals{ServiceRootProduct: "Unknown Generic Redfish Server"})
	if match.Mode != ModeFallback {
		t.Fatalf("expected fallback mode, got %q", match.Mode)
	}
	for _, profile := range match.Profiles {
		if !profile.SafeForFallback() {
			t.Fatalf("fallback mode included non-safe profile %q", profile.Name())
		}
	}
}

func TestBuildAnalysisDirectives_GenericMatchedKeepsFallbacksDisabled(t *testing.T) {
	match := MatchResult{
		Mode:     ModeMatched,
		Profiles: []Profile{genericProfile()},
	}
	directives := ResolveAnalysisPlan(match, nil, DiscoveredResources{}, MatchSignals{}).Directives
	if directives.EnableProcessorGPUFallback {
		t.Fatal("did not expect processor GPU fallback for generic matched profile")
	}
	if directives.EnableSupermicroNVMeBackplane {
		t.Fatal("did not expect supermicro nvme fallback for generic matched profile")
	}
	if directives.EnableGenericGraphicsControllerDedup {
		t.Fatal("did not expect generic graphics-controller dedup for generic matched profile")
	}
}
