package gkeheterogeneous

import (
	"encoding/json"
	"strings"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
)

const (
	ComputeClassCleanupCandidatesAnnotationKey = "tidbcloud.com/gke-compute-class-cleanup-candidates"

	maxComputeClassCleanupCandidates = 32
)

func ComputeClassCleanupCandidates(cluster *cloudv1.Cluster) ([]string, error) {
	if cluster == nil {
		return nil, nil
	}
	raw := strings.TrimSpace(cluster.GetAnnotations()[ComputeClassCleanupCandidatesAnnotationKey])
	if raw == "" {
		return nil, nil
	}
	out := []string{}
	if err := json.Unmarshal([]byte(raw), &out); err != nil {
		return nil, err
	}
	return normalizeComputeClassCleanupCandidates(out), nil
}

func RecordComputeClassCleanupCandidate(cluster *cloudv1.Cluster, computeClassName string) error {
	if cluster == nil || cluster.GetProviderType() != cloudv1.ProviderTypeGKE {
		return nil
	}
	computeClassName = strings.TrimSpace(computeClassName)
	if computeClassName == "" {
		return nil
	}
	candidates, err := ComputeClassCleanupCandidates(cluster)
	if err != nil {
		candidates = nil
	}
	for _, candidate := range candidates {
		if candidate == computeClassName {
			return nil
		}
	}
	candidates = append(candidates, computeClassName)
	return setComputeClassCleanupCandidates(cluster, candidates)
}

func SetComputeClassCleanupCandidates(cluster *cloudv1.Cluster, candidates []string) error {
	return setComputeClassCleanupCandidates(cluster, candidates)
}

func setComputeClassCleanupCandidates(cluster *cloudv1.Cluster, candidates []string) error {
	if cluster == nil {
		return nil
	}
	candidates = normalizeComputeClassCleanupCandidates(candidates)
	value := ""
	if len(candidates) > 0 {
		data, err := json.Marshal(candidates)
		if err != nil {
			return err
		}
		value = string(data)
	}
	cluster.MutateAnnotations(func(annotations map[string]string) {
		if len(candidates) == 0 {
			delete(annotations, ComputeClassCleanupCandidatesAnnotationKey)
			return
		}
		annotations[ComputeClassCleanupCandidatesAnnotationKey] = value
	})
	return nil
}

func normalizeComputeClassCleanupCandidates(candidates []string) []string {
	out := make([]string, 0, len(candidates))
	seen := map[string]struct{}{}
	for _, candidate := range candidates {
		candidate = strings.TrimSpace(candidate)
		if candidate == "" {
			continue
		}
		if _, ok := seen[candidate]; ok {
			continue
		}
		seen[candidate] = struct{}{}
		out = append(out, candidate)
	}
	if len(out) <= maxComputeClassCleanupCandidates {
		return out
	}
	return out[len(out)-maxComputeClassCleanupCandidates:]
}
