package redfishprofile

import (
	"sort"

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

type MatchSignals struct {
	ServiceRootVendor   string
	ServiceRootProduct  string
	SystemManufacturer  string
	SystemModel         string
	SystemSKU           string
	ChassisManufacturer string
	ChassisModel        string
	ManagerManufacturer string
	OEMNamespaces       []string
	ResourceHints       []string
	DocHints            []string
}

type AcquisitionPlan struct {
	Mode          string
	Profiles      []string
	SeedPaths     []string
	CriticalPaths []string
	PlanBPaths    []string
	Notes         []string
	ScopedPaths   AcquisitionScopedPathPolicy
	Tuning        AcquisitionTuning
}

type DiscoveredResources struct {
	SystemPaths  []string
	ChassisPaths []string
	ManagerPaths []string
}

type ResolvedAcquisitionPlan struct {
	Plan          AcquisitionPlan
	SeedPaths     []string
	CriticalPaths []string
}

type AcquisitionScopedPathPolicy struct {
	SystemSeedSuffixes      []string
	SystemCriticalSuffixes  []string
	ChassisSeedSuffixes     []string
	ChassisCriticalSuffixes []string
	ManagerSeedSuffixes     []string
	ManagerCriticalSuffixes []string
}

type AcquisitionTuning struct {
	SnapshotMaxDocuments    int
	SnapshotWorkers         int
	SnapshotExcludeContains []string
	PrefetchEnabled         *bool
	PrefetchWorkers         int
	NVMePostProbeEnabled    *bool
	RatePolicy              AcquisitionRatePolicy
	ETABaseline             AcquisitionETABaseline
	PostProbePolicy         AcquisitionPostProbePolicy
	RecoveryPolicy          AcquisitionRecoveryPolicy
	PrefetchPolicy          AcquisitionPrefetchPolicy
}

type AcquisitionRatePolicy struct {
	TargetP95LatencyMS      int
	ThrottleP95LatencyMS    int
	MinSnapshotWorkers      int
	MinPrefetchWorkers      int
	DisablePrefetchOnErrors bool
}

type AcquisitionETABaseline struct {
	DiscoverySeconds     int
	SnapshotSeconds      int
	PrefetchSeconds      int
	CriticalPlanBSeconds int
	ProfilePlanBSeconds  int
}

type AcquisitionPostProbePolicy struct {
	EnableDirectNVMEDiskBayProbe bool
	EnableNumericCollectionProbe bool
	EnableSensorCollectionProbe  bool
}

type AcquisitionRecoveryPolicy struct {
	EnableCriticalCollectionMemberRetry bool
	EnableCriticalSlowProbe             bool
	EnableProfilePlanB                  bool
	EnableEmptyCriticalCollectionRetry  bool
}

type AcquisitionPrefetchPolicy struct {
	IncludeSuffixes []string
	ExcludeContains []string
}

type AnalysisDirectives struct {
	EnableProcessorGPUFallback           bool
	EnableSupermicroNVMeBackplane        bool
	EnableProcessorGPUChassisAlias       bool
	EnableGenericGraphicsControllerDedup bool
	EnableMSIProcessorGPUChassisLookup   bool
	EnableMSIGhostGPUFilter              bool
	EnableStorageEnclosureRecovery       bool
	EnableKnownStorageControllerRecovery bool
}

type ResolvedAnalysisPlan struct {
	Match                          MatchResult
	Directives                     AnalysisDirectives
	Notes                          []string
	ProcessorGPUChassisLookupModes []string
	KnownStorageDriveCollections   []string
	KnownStorageVolumeCollections  []string
}

type Profile interface {
	Name() string
	Priority() int
	Match(signals MatchSignals) int
	SafeForFallback() bool
	ExtendAcquisitionPlan(plan *AcquisitionPlan, signals MatchSignals)
	RefineAcquisitionPlan(resolved *ResolvedAcquisitionPlan, discovered DiscoveredResources, signals MatchSignals)
	ApplyAnalysisDirectives(directives *AnalysisDirectives, signals MatchSignals)
	RefineAnalysisPlan(plan *ResolvedAnalysisPlan, snapshot map[string]interface{}, discovered DiscoveredResources, signals MatchSignals)
	PostAnalyze(result *models.AnalysisResult, snapshot map[string]interface{}, signals MatchSignals)
}

type MatchResult struct {
	Mode     string
	Profiles []Profile
	Scores   []ProfileScore
}

type ProfileScore struct {
	Name     string
	Score    int
	Active   bool
	Priority int
}

func normalizeSignals(signals MatchSignals) MatchSignals {
	signals.OEMNamespaces = dedupeSorted(signals.OEMNamespaces)
	signals.ResourceHints = dedupeSorted(signals.ResourceHints)
	signals.DocHints = dedupeSorted(signals.DocHints)
	return signals
}

func dedupeSorted(items []string) []string {
	if len(items) == 0 {
		return nil
	}
	set := make(map[string]struct{}, len(items))
	for _, item := range items {
		if item == "" {
			continue
		}
		set[item] = struct{}{}
	}
	out := make([]string, 0, len(set))
	for item := range set {
		out = append(out, item)
	}
	sort.Strings(out)
	return out
}
