package redfishprofile

import (
	"sort"

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

const (
	ModeMatched  = "matched"
	ModeFallback = "fallback"
)

func MatchProfiles(signals MatchSignals) MatchResult {
	type scored struct {
		profile Profile
		score   int
	}
	builtins := BuiltinProfiles()
	candidates := make([]scored, 0, len(builtins))
	allScores := make([]ProfileScore, 0, len(builtins))
	for _, profile := range builtins {
		score := profile.Match(signals)
		allScores = append(allScores, ProfileScore{
			Name:     profile.Name(),
			Score:    score,
			Priority: profile.Priority(),
		})
		if score <= 0 {
			continue
		}
		candidates = append(candidates, scored{profile: profile, score: score})
	}
	sort.Slice(allScores, func(i, j int) bool {
		if allScores[i].Score == allScores[j].Score {
			if allScores[i].Priority == allScores[j].Priority {
				return allScores[i].Name < allScores[j].Name
			}
			return allScores[i].Priority < allScores[j].Priority
		}
		return allScores[i].Score > allScores[j].Score
	})
	sort.Slice(candidates, func(i, j int) bool {
		if candidates[i].score == candidates[j].score {
			return candidates[i].profile.Priority() < candidates[j].profile.Priority()
		}
		return candidates[i].score > candidates[j].score
	})
	if len(candidates) == 0 || candidates[0].score < 60 {
		profiles := make([]Profile, 0, len(builtins))
		active := make(map[string]struct{}, len(builtins))
		for _, profile := range builtins {
			if profile.SafeForFallback() {
				profiles = append(profiles, profile)
				active[profile.Name()] = struct{}{}
			}
		}
		sortProfiles(profiles)
		for i := range allScores {
			_, ok := active[allScores[i].Name]
			allScores[i].Active = ok
		}
		return MatchResult{Mode: ModeFallback, Profiles: profiles, Scores: allScores}
	}
	profiles := make([]Profile, 0, len(candidates))
	seen := make(map[string]struct{}, len(candidates))
	for _, candidate := range candidates {
		name := candidate.profile.Name()
		if _, ok := seen[name]; ok {
			continue
		}
		seen[name] = struct{}{}
		profiles = append(profiles, candidate.profile)
	}
	sortProfiles(profiles)
	for i := range allScores {
		_, ok := seen[allScores[i].Name]
		allScores[i].Active = ok
	}
	return MatchResult{Mode: ModeMatched, Profiles: profiles, Scores: allScores}
}

func BuildAcquisitionPlan(signals MatchSignals) AcquisitionPlan {
	match := MatchProfiles(signals)
	plan := AcquisitionPlan{Mode: match.Mode}
	for _, profile := range match.Profiles {
		plan.Profiles = append(plan.Profiles, profile.Name())
		profile.ExtendAcquisitionPlan(&plan, signals)
	}
	plan.Profiles = dedupeSorted(plan.Profiles)
	plan.SeedPaths = dedupeSorted(plan.SeedPaths)
	plan.CriticalPaths = dedupeSorted(plan.CriticalPaths)
	plan.PlanBPaths = dedupeSorted(plan.PlanBPaths)
	plan.Notes = dedupeSorted(plan.Notes)
	if plan.Mode == ModeFallback {
		ensureSnapshotMaxDocuments(&plan, 180000)
		ensurePrefetchEnabled(&plan, true)
		addPlanNote(&plan, "fallback acquisition expands safe profile probes")
	}
	return plan
}

func ApplyAnalysisProfiles(result *models.AnalysisResult, snapshot map[string]interface{}, signals MatchSignals) MatchResult {
	match := MatchProfiles(signals)
	for _, profile := range match.Profiles {
		profile.PostAnalyze(result, snapshot, signals)
	}
	return match
}

func BuildAnalysisDirectives(match MatchResult) AnalysisDirectives {
	return ResolveAnalysisPlan(match, nil, DiscoveredResources{}, MatchSignals{}).Directives
}

func sortProfiles(profiles []Profile) {
	sort.Slice(profiles, func(i, j int) bool {
		if profiles[i].Priority() == profiles[j].Priority() {
			return profiles[i].Name() < profiles[j].Name()
		}
		return profiles[i].Priority() < profiles[j].Priority()
	})
}
