package redfishprofile

import (
	"strings"

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

type staticProfile struct {
	name                    string
	priority                int
	safeForFallback         bool
	matchFn                 func(MatchSignals) int
	extendAcquisition       func(*AcquisitionPlan, MatchSignals)
	refineAcquisition       func(*ResolvedAcquisitionPlan, DiscoveredResources, MatchSignals)
	applyAnalysisDirectives func(*AnalysisDirectives, MatchSignals)
	refineAnalysis          func(*ResolvedAnalysisPlan, map[string]interface{}, DiscoveredResources, MatchSignals)
	postAnalyze             func(*models.AnalysisResult, map[string]interface{}, MatchSignals)
}

func (p staticProfile) Name() string                   { return p.name }
func (p staticProfile) Priority() int                  { return p.priority }
func (p staticProfile) Match(signals MatchSignals) int { return p.matchFn(normalizeSignals(signals)) }
func (p staticProfile) SafeForFallback() bool          { return p.safeForFallback }
func (p staticProfile) ExtendAcquisitionPlan(plan *AcquisitionPlan, signals MatchSignals) {
	if p.extendAcquisition != nil {
		p.extendAcquisition(plan, normalizeSignals(signals))
	}
}
func (p staticProfile) RefineAcquisitionPlan(resolved *ResolvedAcquisitionPlan, discovered DiscoveredResources, signals MatchSignals) {
	if p.refineAcquisition != nil {
		p.refineAcquisition(resolved, discovered, normalizeSignals(signals))
	}
}
func (p staticProfile) ApplyAnalysisDirectives(directives *AnalysisDirectives, signals MatchSignals) {
	if p.applyAnalysisDirectives != nil {
		p.applyAnalysisDirectives(directives, normalizeSignals(signals))
	}
}
func (p staticProfile) RefineAnalysisPlan(plan *ResolvedAnalysisPlan, snapshot map[string]interface{}, discovered DiscoveredResources, signals MatchSignals) {
	if p.refineAnalysis != nil {
		p.refineAnalysis(plan, snapshot, discovered, normalizeSignals(signals))
	}
}
func (p staticProfile) PostAnalyze(result *models.AnalysisResult, snapshot map[string]interface{}, signals MatchSignals) {
	if p.postAnalyze != nil {
		p.postAnalyze(result, snapshot, normalizeSignals(signals))
	}
}

func BuiltinProfiles() []Profile {
	return []Profile{
		genericProfile(),
		amiProfile(),
		msiProfile(),
		supermicroProfile(),
		dellProfile(),
		hpeProfile(),
		lenovoProfile(),
		inspurGroupOEMPlatformsProfile(),
		hgxProfile(),
		xfusionProfile(),
	}
}

func containsFold(v, sub string) bool {
	return strings.Contains(strings.ToLower(strings.TrimSpace(v)), strings.ToLower(strings.TrimSpace(sub)))
}

func addPlanPaths(dst *[]string, paths ...string) {
	*dst = append(*dst, paths...)
	*dst = dedupeSorted(*dst)
}

func addPlanNote(plan *AcquisitionPlan, note string) {
	if strings.TrimSpace(note) == "" {
		return
	}
	plan.Notes = append(plan.Notes, note)
	plan.Notes = dedupeSorted(plan.Notes)
}

func addAnalysisNote(plan *ResolvedAnalysisPlan, note string) {
	if plan == nil || strings.TrimSpace(note) == "" {
		return
	}
	plan.Notes = append(plan.Notes, note)
	plan.Notes = dedupeSorted(plan.Notes)
}

func addAnalysisLookupMode(plan *ResolvedAnalysisPlan, mode string) {
	if plan == nil || strings.TrimSpace(mode) == "" {
		return
	}
	plan.ProcessorGPUChassisLookupModes = dedupeSorted(append(plan.ProcessorGPUChassisLookupModes, mode))
}

func addAnalysisStorageDriveCollections(plan *ResolvedAnalysisPlan, rels ...string) {
	if plan == nil {
		return
	}
	plan.KnownStorageDriveCollections = dedupeSorted(append(plan.KnownStorageDriveCollections, rels...))
}

func addAnalysisStorageVolumeCollections(plan *ResolvedAnalysisPlan, rels ...string) {
	if plan == nil {
		return
	}
	plan.KnownStorageVolumeCollections = dedupeSorted(append(plan.KnownStorageVolumeCollections, rels...))
}

func ensureSnapshotMaxDocuments(plan *AcquisitionPlan, n int) {
	if n <= 0 {
		return
	}
	if plan.Tuning.SnapshotMaxDocuments < n {
		plan.Tuning.SnapshotMaxDocuments = n
	}
}

func ensureSnapshotWorkers(plan *AcquisitionPlan, n int) {
	if n <= 0 {
		return
	}
	if plan.Tuning.SnapshotWorkers < n {
		plan.Tuning.SnapshotWorkers = n
	}
}

func ensurePrefetchEnabled(plan *AcquisitionPlan, enabled bool) {
	if plan.Tuning.PrefetchEnabled == nil {
		plan.Tuning.PrefetchEnabled = new(bool)
	}
	*plan.Tuning.PrefetchEnabled = enabled
}

func ensurePrefetchWorkers(plan *AcquisitionPlan, n int) {
	if n <= 0 {
		return
	}
	if plan.Tuning.PrefetchWorkers < n {
		plan.Tuning.PrefetchWorkers = n
	}
}

func ensureNVMePostProbeEnabled(plan *AcquisitionPlan, enabled bool) {
	if plan.Tuning.NVMePostProbeEnabled == nil {
		plan.Tuning.NVMePostProbeEnabled = new(bool)
	}
	*plan.Tuning.NVMePostProbeEnabled = enabled
}

func ensureRatePolicy(plan *AcquisitionPlan, policy AcquisitionRatePolicy) {
	if policy.TargetP95LatencyMS > plan.Tuning.RatePolicy.TargetP95LatencyMS {
		plan.Tuning.RatePolicy.TargetP95LatencyMS = policy.TargetP95LatencyMS
	}
	if policy.ThrottleP95LatencyMS > plan.Tuning.RatePolicy.ThrottleP95LatencyMS {
		plan.Tuning.RatePolicy.ThrottleP95LatencyMS = policy.ThrottleP95LatencyMS
	}
	if policy.MinSnapshotWorkers > plan.Tuning.RatePolicy.MinSnapshotWorkers {
		plan.Tuning.RatePolicy.MinSnapshotWorkers = policy.MinSnapshotWorkers
	}
	if policy.MinPrefetchWorkers > plan.Tuning.RatePolicy.MinPrefetchWorkers {
		plan.Tuning.RatePolicy.MinPrefetchWorkers = policy.MinPrefetchWorkers
	}
	if policy.DisablePrefetchOnErrors {
		plan.Tuning.RatePolicy.DisablePrefetchOnErrors = true
	}
}

func ensureETABaseline(plan *AcquisitionPlan, baseline AcquisitionETABaseline) {
	if baseline.DiscoverySeconds > plan.Tuning.ETABaseline.DiscoverySeconds {
		plan.Tuning.ETABaseline.DiscoverySeconds = baseline.DiscoverySeconds
	}
	if baseline.SnapshotSeconds > plan.Tuning.ETABaseline.SnapshotSeconds {
		plan.Tuning.ETABaseline.SnapshotSeconds = baseline.SnapshotSeconds
	}
	if baseline.PrefetchSeconds > plan.Tuning.ETABaseline.PrefetchSeconds {
		plan.Tuning.ETABaseline.PrefetchSeconds = baseline.PrefetchSeconds
	}
	if baseline.CriticalPlanBSeconds > plan.Tuning.ETABaseline.CriticalPlanBSeconds {
		plan.Tuning.ETABaseline.CriticalPlanBSeconds = baseline.CriticalPlanBSeconds
	}
	if baseline.ProfilePlanBSeconds > plan.Tuning.ETABaseline.ProfilePlanBSeconds {
		plan.Tuning.ETABaseline.ProfilePlanBSeconds = baseline.ProfilePlanBSeconds
	}
}

func ensurePostProbePolicy(plan *AcquisitionPlan, policy AcquisitionPostProbePolicy) {
	if policy.EnableDirectNVMEDiskBayProbe {
		plan.Tuning.PostProbePolicy.EnableDirectNVMEDiskBayProbe = true
	}
	if policy.EnableNumericCollectionProbe {
		plan.Tuning.PostProbePolicy.EnableNumericCollectionProbe = true
	}
	if policy.EnableSensorCollectionProbe {
		plan.Tuning.PostProbePolicy.EnableSensorCollectionProbe = true
	}
}

func ensureRecoveryPolicy(plan *AcquisitionPlan, policy AcquisitionRecoveryPolicy) {
	if policy.EnableCriticalCollectionMemberRetry {
		plan.Tuning.RecoveryPolicy.EnableCriticalCollectionMemberRetry = true
	}
	if policy.EnableCriticalSlowProbe {
		plan.Tuning.RecoveryPolicy.EnableCriticalSlowProbe = true
	}
	if policy.EnableProfilePlanB {
		plan.Tuning.RecoveryPolicy.EnableProfilePlanB = true
	}
	if policy.EnableEmptyCriticalCollectionRetry {
		plan.Tuning.RecoveryPolicy.EnableEmptyCriticalCollectionRetry = true
	}
}

func ensureScopedPathPolicy(plan *AcquisitionPlan, policy AcquisitionScopedPathPolicy) {
	addPlanPaths(&plan.ScopedPaths.SystemSeedSuffixes, policy.SystemSeedSuffixes...)
	addPlanPaths(&plan.ScopedPaths.SystemCriticalSuffixes, policy.SystemCriticalSuffixes...)
	addPlanPaths(&plan.ScopedPaths.ChassisSeedSuffixes, policy.ChassisSeedSuffixes...)
	addPlanPaths(&plan.ScopedPaths.ChassisCriticalSuffixes, policy.ChassisCriticalSuffixes...)
	addPlanPaths(&plan.ScopedPaths.ManagerSeedSuffixes, policy.ManagerSeedSuffixes...)
	addPlanPaths(&plan.ScopedPaths.ManagerCriticalSuffixes, policy.ManagerCriticalSuffixes...)
}

func ensurePrefetchPolicy(plan *AcquisitionPlan, policy AcquisitionPrefetchPolicy) {
	addPlanPaths(&plan.Tuning.PrefetchPolicy.IncludeSuffixes, policy.IncludeSuffixes...)
	addPlanPaths(&plan.Tuning.PrefetchPolicy.ExcludeContains, policy.ExcludeContains...)
}

func ensureSnapshotExcludeContains(plan *AcquisitionPlan, patterns ...string) {
	addPlanPaths(&plan.Tuning.SnapshotExcludeContains, patterns...)
}

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