package redfishprofile

import "strings"

func ResolveAcquisitionPlan(match MatchResult, plan AcquisitionPlan, discovered DiscoveredResources, signals MatchSignals) ResolvedAcquisitionPlan {
	seedGroups := [][]string{
		baselineSeedPaths(discovered),
		expandScopedSuffixes(discovered.SystemPaths, plan.ScopedPaths.SystemSeedSuffixes),
		expandScopedSuffixes(discovered.ChassisPaths, plan.ScopedPaths.ChassisSeedSuffixes),
		expandScopedSuffixes(discovered.ManagerPaths, plan.ScopedPaths.ManagerSeedSuffixes),
		plan.SeedPaths,
	}
	if plan.Mode == ModeFallback {
		seedGroups = append(seedGroups, plan.PlanBPaths)
	}

	criticalGroups := [][]string{
		baselineCriticalPaths(discovered),
		expandScopedSuffixes(discovered.SystemPaths, plan.ScopedPaths.SystemCriticalSuffixes),
		expandScopedSuffixes(discovered.ChassisPaths, plan.ScopedPaths.ChassisCriticalSuffixes),
		expandScopedSuffixes(discovered.ManagerPaths, plan.ScopedPaths.ManagerCriticalSuffixes),
		plan.CriticalPaths,
	}

	resolved := ResolvedAcquisitionPlan{
		Plan:          plan,
		SeedPaths:     mergeResolvedPaths(seedGroups...),
		CriticalPaths: mergeResolvedPaths(criticalGroups...),
	}
	for _, profile := range match.Profiles {
		profile.RefineAcquisitionPlan(&resolved, discovered, signals)
	}
	resolved.SeedPaths = mergeResolvedPaths(resolved.SeedPaths)
	resolved.CriticalPaths = mergeResolvedPaths(resolved.CriticalPaths, resolved.Plan.CriticalPaths)
	resolved.Plan.SeedPaths = mergeResolvedPaths(resolved.Plan.SeedPaths)
	resolved.Plan.CriticalPaths = mergeResolvedPaths(resolved.Plan.CriticalPaths)
	resolved.Plan.PlanBPaths = mergeResolvedPaths(resolved.Plan.PlanBPaths)
	return resolved
}

func baselineSeedPaths(discovered DiscoveredResources) []string {
	var out []string
	add := func(p string) {
		if p = normalizePath(p); p != "" {
			out = append(out, p)
		}
	}

	add("/redfish/v1/UpdateService")
	add("/redfish/v1/UpdateService/FirmwareInventory")

	for _, p := range discovered.SystemPaths {
		add(p)
		add(joinPath(p, "/Bios"))
		add(joinPath(p, "/Oem/Public"))
		add(joinPath(p, "/Oem/Public/FRU"))
		add(joinPath(p, "/Processors"))
		add(joinPath(p, "/Memory"))
		add(joinPath(p, "/EthernetInterfaces"))
		add(joinPath(p, "/NetworkInterfaces"))
		add(joinPath(p, "/PCIeDevices"))
		add(joinPath(p, "/PCIeFunctions"))
		add(joinPath(p, "/Accelerators"))
		add(joinPath(p, "/GraphicsControllers"))
		add(joinPath(p, "/Storage"))
	}
	for _, p := range discovered.ChassisPaths {
		add(p)
		add(joinPath(p, "/Oem/Public"))
		add(joinPath(p, "/Oem/Public/FRU"))
		add(joinPath(p, "/PCIeDevices"))
		add(joinPath(p, "/PCIeSlots"))
		add(joinPath(p, "/NetworkAdapters"))
		add(joinPath(p, "/Drives"))
		add(joinPath(p, "/Power"))
	}
	for _, p := range discovered.ManagerPaths {
		add(p)
		add(joinPath(p, "/EthernetInterfaces"))
		add(joinPath(p, "/NetworkProtocol"))
	}
	return mergeResolvedPaths(out)
}

func baselineCriticalPaths(discovered DiscoveredResources) []string {
	var out []string
	for _, group := range [][]string{
		{"/redfish/v1"},
		discovered.SystemPaths,
		discovered.ChassisPaths,
		discovered.ManagerPaths,
	} {
		out = append(out, group...)
	}
	return mergeResolvedPaths(out)
}

func expandScopedSuffixes(basePaths, suffixes []string) []string {
	if len(basePaths) == 0 || len(suffixes) == 0 {
		return nil
	}
	out := make([]string, 0, len(basePaths)*len(suffixes))
	for _, basePath := range basePaths {
		basePath = normalizePath(basePath)
		if basePath == "" {
			continue
		}
		for _, suffix := range suffixes {
			suffix = strings.TrimSpace(suffix)
			if suffix == "" {
				continue
			}
			out = append(out, joinPath(basePath, suffix))
		}
	}
	return mergeResolvedPaths(out)
}

func mergeResolvedPaths(groups ...[]string) []string {
	seen := make(map[string]struct{})
	out := make([]string, 0)
	for _, group := range groups {
		for _, path := range group {
			path = normalizePath(path)
			if path == "" {
				continue
			}
			if _, ok := seen[path]; ok {
				continue
			}
			seen[path] = struct{}{}
			out = append(out, path)
		}
	}
	return out
}

func normalizePath(path string) string {
	path = strings.TrimSpace(path)
	if path == "" {
		return ""
	}
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}
	return strings.TrimRight(path, "/")
}

func joinPath(base, rel string) string {
	base = normalizePath(base)
	rel = strings.TrimSpace(rel)
	if base == "" {
		return normalizePath(rel)
	}
	if rel == "" {
		return base
	}
	if !strings.HasPrefix(rel, "/") {
		rel = "/" + rel
	}
	return normalizePath(base + rel)
}
