/*
Copyright 2025 The KubeFleet Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tests

// This file features utilities used in the test suites.

import (
	"fmt"
	"strconv"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	. "github.com/onsi/gomega"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/manager"

	clusterv1beta1 "go.goms.io/fleet/apis/cluster/v1beta1"
	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
	"go.goms.io/fleet/pkg/scheduler/clustereligibilitychecker"
	"go.goms.io/fleet/pkg/scheduler/framework"
	"go.goms.io/fleet/pkg/scheduler/framework/plugins/clusteraffinity"
	"go.goms.io/fleet/pkg/scheduler/framework/plugins/clustereligibility"
	"go.goms.io/fleet/pkg/scheduler/framework/plugins/sameplacementaffinity"
	"go.goms.io/fleet/pkg/scheduler/framework/plugins/tainttoleration"
	"go.goms.io/fleet/pkg/scheduler/framework/plugins/topologyspreadconstraints"
)

// This file features some utilities used in the test suites.

const (
	crpNameTemplate                = "crp-%d"
	rpNameTemplate                 = "rp-%d"
	policySnapshotNameTemplate     = "%s-policy-snapshot-%d"
	provisionalClusterNameTemplate = "provisional-cluster-%d"

	policyHash = "policy-hash"

	bindingNamePlaceholder = "binding"

	testNamespace = "test-namespace"
)

var (
	// Note that for the scheduler integration tests, since no actual resources are collected
	// by any controller (the scheduler cares only about policy snapshots and manipulates
	// bindings accordingly), it is safe for all suites to select the same set of resources
	// (which is not even provisioned in the environment).
	defaultResourceSelectors = []placementv1beta1.ResourceSelectorTerm{
		{
			Group:   "core",
			Kind:    "Namespace",
			Version: "v1",
			Name:    "work",
		},
	}

	nilScoreByCluster = map[string]*placementv1beta1.ClusterScore{}
	zeroScore         = placementv1beta1.ClusterScore{
		AffinityScore:       ptr.To(int32(0)),
		TopologySpreadScore: ptr.To(int32(0)),
	}
	zeroScoreByCluster = map[string]*placementv1beta1.ClusterScore{
		memberCluster1EastProd:          &zeroScore,
		memberCluster2EastProd:          &zeroScore,
		memberCluster3EastCanary:        &zeroScore,
		memberCluster4CentralProd:       &zeroScore,
		memberCluster5CentralProd:       &zeroScore,
		memberCluster6WestProd:          &zeroScore,
		memberCluster7WestCanary:        &zeroScore,
		memberCluster8UnhealthyEastProd: &zeroScore,
		memberCluster9LeftCentralProd:   &zeroScore,
	}
)

var (
	lessFuncBinding = func(binding1, binding2 placementv1beta1.BindingObj) bool {
		return binding1.GetBindingSpec().TargetCluster < binding2.GetBindingSpec().TargetCluster
	}
	lessFuncClusterDecision = func(decision1, decision2 placementv1beta1.ClusterDecision) bool {
		return decision1.ClusterName < decision2.ClusterName
	}
	ignoreUnselectedClusterDecision = func(decision placementv1beta1.ClusterDecision) bool {
		return !decision.Selected
	}

	ignoreClusterDecisionReasonField          = cmpopts.IgnoreFields(placementv1beta1.ClusterDecision{}, "Reason")
	ignoreObjectMetaNameField                 = cmpopts.IgnoreFields(metav1.ObjectMeta{}, "Name")
	ignoreObjectMetaAnnotationField           = cmpopts.IgnoreFields(metav1.ObjectMeta{}, "Annotations")
	ignoreObjectMetaAutoGeneratedFields       = cmpopts.IgnoreFields(metav1.ObjectMeta{}, "UID", "CreationTimestamp", "ResourceVersion", "Generation", "ManagedFields")
	ignoreClusterResourceBindingTypeMetaField = cmpopts.IgnoreFields(placementv1beta1.ClusterResourceBinding{}, "TypeMeta")
	ignoreResourceBindingTypeMetaField        = cmpopts.IgnoreFields(placementv1beta1.ResourceBinding{}, "TypeMeta")
	ignoreConditionTimeReasonAndMessageFields = cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime", "Reason", "Message")

	ignoreResourceBindingFields = []cmp.Option{
		ignoreClusterResourceBindingTypeMetaField,
		ignoreResourceBindingTypeMetaField,
		ignoreObjectMetaNameField,
		ignoreObjectMetaAnnotationField,
		ignoreObjectMetaAutoGeneratedFields,
		ignoreClusterDecisionReasonField,
		cmpopts.SortSlices(lessFuncBinding),
	}
)

func buildSchedulerFramework(ctrlMgr manager.Manager, clusterEligibilityChecker *clustereligibilitychecker.ClusterEligibilityChecker) framework.Framework {
	// Create a new profile.
	profile := framework.NewProfile(defaultProfileName)

	// Register the plugins.
	clusterAffinityPlugin := clusteraffinity.New()
	clustereligibilityPlugin := clustereligibility.New()
	taintTolerationPlugin := tainttoleration.New()
	samePlacementAffinityPlugin := sameplacementaffinity.New()
	topologyspreadconstraintsPlugin := topologyspreadconstraints.New()
	profile.
		// Register cluster affinity plugin.
		WithPreFilterPlugin(&clusterAffinityPlugin).
		WithFilterPlugin(&clusterAffinityPlugin).
		WithPreScorePlugin(&clusterAffinityPlugin).
		WithScorePlugin(&clusterAffinityPlugin).
		// Register cluster eligibility plugin.
		WithFilterPlugin(&clustereligibilityPlugin).
		// Register taint toleration plugin.
		WithFilterPlugin(&taintTolerationPlugin).
		// Register same placement affinity plugin.
		WithFilterPlugin(&samePlacementAffinityPlugin).
		WithScorePlugin(&samePlacementAffinityPlugin).
		// Register topology spread constraints plugin.
		WithPostBatchPlugin(&topologyspreadconstraintsPlugin).
		WithPreFilterPlugin(&topologyspreadconstraintsPlugin).
		WithFilterPlugin(&topologyspreadconstraintsPlugin).
		WithPreScorePlugin(&topologyspreadconstraintsPlugin).
		WithScorePlugin(&topologyspreadconstraintsPlugin)

	// Create a scheduler framework.
	return framework.NewFramework(profile, ctrlMgr, framework.WithClusterEligibilityChecker(clusterEligibilityChecker))
}

func buildK8sAPIConfigFrom(restCfg *rest.Config) []byte {
	clusterName := "default-cluster"
	contextName := "default-context"
	userName := "admin"

	clusters := make(map[string]*clientcmdapi.Cluster)
	clusters[clusterName] = &clientcmdapi.Cluster{
		Server:                   restCfg.Host,
		CertificateAuthorityData: restCfg.CAData,
	}

	contexts := make(map[string]*clientcmdapi.Context)
	contexts[contextName] = &clientcmdapi.Context{
		Cluster:  clusterName,
		AuthInfo: userName,
	}

	authInfos := make(map[string]*clientcmdapi.AuthInfo)
	authInfos[userName] = &clientcmdapi.AuthInfo{
		ClientCertificateData: restCfg.CertData,
		ClientKeyData:         restCfg.KeyData,
	}

	apiCfg := &clientcmdapi.Config{
		Kind:           "Config",
		APIVersion:     "v1",
		Clusters:       clusters,
		Contexts:       contexts,
		CurrentContext: contextName,
		AuthInfos:      authInfos,
	}

	apiCfgBytes, err := clientcmd.Write(*apiCfg)
	Expect(err).To(BeNil(), "Failed to write API config")
	return apiCfgBytes
}

func loadRestConfigFrom(apiCfgBytes []byte) *rest.Config {
	apiCfg, err := clientcmd.Load(apiCfgBytes)
	Expect(err).To(BeNil(), "Failed to load API config")

	restCfg, err := clientcmd.NewDefaultClientConfig(*apiCfg, &clientcmd.ConfigOverrides{}).ClientConfig()
	Expect(err).To(BeNil(), "Failed to load REST config")

	return restCfg
}

func createMemberCluster(name string, taints []clusterv1beta1.Taint) {
	memberCluster := clusterv1beta1.MemberCluster{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Spec: clusterv1beta1.MemberClusterSpec{
			Identity: rbacv1.Subject{
				Kind:     "ServiceAccount",
				APIGroup: "",
				Name:     "admin",
			},
			Taints: taints,
		},
	}
	Expect(hubClient.Create(ctx, &memberCluster)).To(Succeed(), "Failed to create member cluster")
}

func markClusterAsHealthy(name string) {
	memberCluster := clusterv1beta1.MemberCluster{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: name}, &memberCluster)).To(Succeed(), "Failed to get member cluster")
	memberCluster.Status.AgentStatus = []clusterv1beta1.AgentStatus{
		{
			Type: clusterv1beta1.MemberAgent,
			Conditions: []metav1.Condition{
				{
					Type:               string(clusterv1beta1.AgentJoined),
					Status:             metav1.ConditionTrue,
					LastTransitionTime: metav1.NewTime(time.Now()),
					Reason:             dummyReason,
				},
				{
					Type:               string(clusterv1beta1.AgentHealthy),
					Status:             metav1.ConditionTrue,
					LastTransitionTime: metav1.NewTime(time.Now()),
					Reason:             dummyReason,
				},
			},
			LastReceivedHeartbeat: metav1.NewTime(time.Now()),
		},
	}
	Expect(hubClient.Status().Update(ctx, &memberCluster)).To(Succeed(), "Failed to update member cluster status")
}

func markClusterAsUnhealthy(name string) {
	memberCluster := clusterv1beta1.MemberCluster{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: name}, &memberCluster)).To(Succeed(), "Failed to get member cluster")
	memberCluster.Status.AgentStatus = []clusterv1beta1.AgentStatus{
		{
			Type: clusterv1beta1.MemberAgent,
			Conditions: []metav1.Condition{
				{
					Type:               string(clusterv1beta1.AgentJoined),
					Status:             metav1.ConditionTrue,
					LastTransitionTime: metav1.NewTime(time.Now().Add(-time.Hour * 25)),
					Reason:             dummyReason,
				},
				{
					Type:               string(clusterv1beta1.AgentHealthy),
					Status:             metav1.ConditionTrue,
					LastTransitionTime: metav1.NewTime(time.Now().Add(-time.Hour * 25)),
					Reason:             dummyReason,
				},
			},
			LastReceivedHeartbeat: metav1.NewTime(time.Now().Add(-time.Hour * 25)),
		},
	}
	Expect(hubClient.Status().Update(ctx, &memberCluster)).To(Succeed(), "Failed to update member cluster status")
}

func createPickFixedCRPWithPolicySnapshot(crpName string, targetClusters []string, policySnapshotName string) {
	policy := &placementv1beta1.PlacementPolicy{
		PlacementType: placementv1beta1.PickFixedPlacementType,
		ClusterNames:  targetClusters,
	}

	// Create the CRP.
	crp := &placementv1beta1.ClusterResourcePlacement{
		ObjectMeta: metav1.ObjectMeta{
			Name:       crpName,
			Finalizers: []string{customDeletionBlockerFinalizer},
		},
		Spec: placementv1beta1.PlacementSpec{
			ResourceSelectors: defaultResourceSelectors,
			Policy:            policy,
		},
	}
	Expect(hubClient.Create(ctx, crp)).To(Succeed(), "Failed to create CRP")

	crpGeneration := crp.Generation

	// Create the associated policy snapshot.
	policySnapshot := &placementv1beta1.ClusterSchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name: policySnapshotName,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: crpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(crpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).To(Succeed(), "Failed to create policy snapshot")
}

func createPickFixedRPWithPolicySnapshot(namespace, rpName string, targetClusters []string, policySnapshotName string) {
	policy := &placementv1beta1.PlacementPolicy{
		PlacementType: placementv1beta1.PickFixedPlacementType,
		ClusterNames:  targetClusters,
	}

	// Create the RP.
	rp := &placementv1beta1.ResourcePlacement{
		ObjectMeta: metav1.ObjectMeta{
			Name:       rpName,
			Namespace:  namespace,
			Finalizers: []string{customDeletionBlockerFinalizer},
		},
		Spec: placementv1beta1.PlacementSpec{
			ResourceSelectors: defaultResourceSelectors,
			Policy:            policy,
		},
	}
	Expect(hubClient.Create(ctx, rp)).To(Succeed(), "Failed to create CRP")

	rpGeneration := rp.Generation

	// Create the associated policy snapshot.
	policySnapshot := &placementv1beta1.SchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name:      policySnapshotName,
			Namespace: namespace,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: rpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(rpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).To(Succeed(), "Failed to create policy snapshot")
}

func createNilSchedulingPolicyCRPWithPolicySnapshot(crpName string, policySnapshotName string, policy *placementv1beta1.PlacementPolicy) {
	// Create a CRP with no scheduling policy specified.
	crp := placementv1beta1.ClusterResourcePlacement{
		ObjectMeta: metav1.ObjectMeta{
			Name:       crpName,
			Finalizers: []string{customDeletionBlockerFinalizer},
		},
		Spec: placementv1beta1.PlacementSpec{
			ResourceSelectors: defaultResourceSelectors,
			Policy:            policy,
		},
	}
	Expect(hubClient.Create(ctx, &crp)).Should(Succeed(), "Failed to create CRP")

	crpGeneration := crp.Generation

	// Create the associated policy snapshot.
	policySnapshot := &placementv1beta1.ClusterSchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name: policySnapshotName,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: crpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(crpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).Should(Succeed(), "Failed to create policy snapshot")
}

func createNilSchedulingPolicyRPWithPolicySnapshot(namespace, rpName string, policySnapshotName string, policy *placementv1beta1.PlacementPolicy) {
	// Create a RP with no scheduling policy specified.
	rp := placementv1beta1.ResourcePlacement{
		ObjectMeta: metav1.ObjectMeta{
			Name:       rpName,
			Namespace:  namespace,
			Finalizers: []string{customDeletionBlockerFinalizer},
		},
		Spec: placementv1beta1.PlacementSpec{
			ResourceSelectors: defaultResourceSelectors,
			Policy:            policy,
		},
	}
	Expect(hubClient.Create(ctx, &rp)).Should(Succeed(), "Failed to create RP")

	rpGeneration := rp.Generation

	// Create the associated policy snapshot.
	policySnapshot := &placementv1beta1.SchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name:      policySnapshotName,
			Namespace: namespace,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: rpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(rpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).Should(Succeed(), "Failed to create policy snapshot")
}

func updatePickFixedCRPWithNewTargetClustersAndRefreshSnapshots(crpName string, targetClusters []string, oldPolicySnapshotName, newPolicySnapshotName string) {
	// Update the CRP.
	crp := &placementv1beta1.ClusterResourcePlacement{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: crpName}, crp)).To(Succeed(), "Failed to get CRP")

	policy := crp.Spec.Policy.DeepCopy()
	policy.ClusterNames = targetClusters
	crp.Spec.Policy = policy
	Expect(hubClient.Update(ctx, crp)).To(Succeed(), "Failed to update CRP")

	crpGeneration := crp.Generation

	// Mark the old policy snapshot as inactive.
	policySnapshot := &placementv1beta1.ClusterSchedulingPolicySnapshot{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: oldPolicySnapshotName}, policySnapshot)).To(Succeed(), "Failed to get policy snapshot")
	policySnapshot.Labels[placementv1beta1.IsLatestSnapshotLabel] = strconv.FormatBool(false)
	Expect(hubClient.Update(ctx, policySnapshot)).To(Succeed(), "Failed to update policy snapshot")

	// Create a new policy snapshot.
	policySnapshot = &placementv1beta1.ClusterSchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name: newPolicySnapshotName,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: crpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(crpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).To(Succeed(), "Failed to create policy snapshot")
}

func updatePickFixedRPWithNewTargetClustersAndRefreshSnapshots(namespace, rpName string, targetClusters []string, oldPolicySnapshotName, newPolicySnapshotName string) {
	// Update the RP.
	rp := &placementv1beta1.ResourcePlacement{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: rpName, Namespace: namespace}, rp)).To(Succeed(), "Failed to get RP")

	policy := rp.Spec.Policy.DeepCopy()
	policy.ClusterNames = targetClusters
	rp.Spec.Policy = policy
	Expect(hubClient.Update(ctx, rp)).To(Succeed(), "Failed to update RP")

	rpGeneration := rp.Generation

	// Mark the old policy snapshot as inactive.
	policySnapshot := &placementv1beta1.SchedulingPolicySnapshot{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: oldPolicySnapshotName, Namespace: namespace}, policySnapshot)).To(Succeed(), "Failed to get policy snapshot")
	policySnapshot.Labels[placementv1beta1.IsLatestSnapshotLabel] = strconv.FormatBool(false)
	Expect(hubClient.Update(ctx, policySnapshot)).To(Succeed(), "Failed to update policy snapshot")

	// Create a new policy snapshot.
	policySnapshot = &placementv1beta1.SchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name:      newPolicySnapshotName,
			Namespace: namespace,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: rpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(rpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).To(Succeed(), "Failed to create policy snapshot")
}

func markBindingsAsBoundForClusters(placementKey types.NamespacedName, boundClusters []string) {
	bindingList, err := listBindings(placementKey)
	Expect(err).ToNot(HaveOccurred(), "Failed to list bindings")

	boundClusterMap := make(map[string]bool)
	for _, cluster := range boundClusters {
		boundClusterMap[cluster] = true
	}

	for _, bindingObj := range bindingList.GetBindingObjs() {
		if _, ok := boundClusterMap[bindingObj.GetBindingSpec().TargetCluster]; ok && bindingObj.GetBindingSpec().State == placementv1beta1.BindingStateScheduled {
			bindingObj.GetBindingSpec().State = placementv1beta1.BindingStateBound
			Expect(hubClient.Update(ctx, bindingObj)).To(Succeed(), "Failed to update binding")
		}
	}
}

func ensurePlacementAndAllRelatedResourcesDeletion(placementKey types.NamespacedName) {
	namespace, placementName := placementKey.Namespace, placementKey.Name
	// Delete the placement.
	var placement placementv1beta1.PlacementObj
	if namespace == "" {
		// Delete CRP.
		placement = &placementv1beta1.ClusterResourcePlacement{
			ObjectMeta: metav1.ObjectMeta{
				Name: placementName,
			},
		}
	} else {
		// Delete RP.
		placement = &placementv1beta1.ResourcePlacement{
			ObjectMeta: metav1.ObjectMeta{
				Name:      placementName,
				Namespace: namespace,
			},
		}
	}
	Expect(hubClient.Delete(ctx, placement)).To(Succeed(), "Failed to delete placement")

	// Ensure that all the bindings are deleted.
	noBindingsCreatedActual := noBindingsCreatedForPlacementActual(placementKey)
	Eventually(noBindingsCreatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to clear all bindings")
	Consistently(noBindingsCreatedActual, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Failed to clear all bindings")

	// Ensure that the scheduler finalizer is removed.
	finalizerRemovedActual := placementSchedulerFinalizerRemovedActual(placementKey)
	Eventually(finalizerRemovedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to remove scheduler cleanup finalizer from placement")

	// Remove all the other finalizers from the placement.
	Eventually(func() error {
		if err := hubClient.Get(ctx, placementKey, placement); err != nil {
			return err
		}

		placement.SetFinalizers([]string{})
		return hubClient.Update(ctx, placement)
	}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to remove all finalizers from placement")

	// Ensure that the placement is deleted.
	Eventually(func() error {
		err := hubClient.Get(ctx, placementKey, placement)
		if errors.IsNotFound(err) {
			return nil
		}

		return err
	}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to delete placement")

	// List all policy snapshots.
	var policySnapshotList placementv1beta1.PolicySnapshotList
	labelSelector := labels.SelectorFromSet(labels.Set{placementv1beta1.PlacementTrackingLabel: placementName})
	listOptions := &client.ListOptions{LabelSelector: labelSelector}

	if namespace == "" {
		// List CSPS.
		policySnapshotList = &placementv1beta1.ClusterSchedulingPolicySnapshotList{}
	} else {
		// List SPS.
		policySnapshotList = &placementv1beta1.SchedulingPolicySnapshotList{}
		listOptions.Namespace = namespace
	}
	Expect(hubClient.List(ctx, policySnapshotList, listOptions)).To(Succeed(), "Failed to list policy snapshots")

	// Delete all policy snapshots and ensure their deletion.
	for _, policySnapshot := range policySnapshotList.GetPolicySnapshotObjs() {
		Expect(hubClient.Delete(ctx, policySnapshot)).To(Succeed(), "Failed to delete policy snapshot")

		Eventually(func() error {
			var ps placementv1beta1.PolicySnapshotObj
			if namespace == "" {
				ps = &placementv1beta1.ClusterSchedulingPolicySnapshot{}
			} else {
				ps = &placementv1beta1.SchedulingPolicySnapshot{}
			}
			err := hubClient.Get(ctx, types.NamespacedName{Name: policySnapshot.GetName(), Namespace: policySnapshot.GetNamespace()}, ps)
			if errors.IsNotFound(err) {
				return nil
			}

			return err
		}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to delete policy snapshot")
	}
}

func ensureProvisionalClusterDeletion(clusterName string) {
	// Retrieve the provisional cluster.
	memberCluster := &clusterv1beta1.MemberCluster{
		ObjectMeta: metav1.ObjectMeta{
			Name: clusterName,
		},
	}
	Expect(hubClient.Delete(ctx, memberCluster)).To(Succeed(), "Failed to delete member cluster")

	// Ensure that the provisional cluster is deleted.
	Eventually(func() error {
		err := hubClient.Get(ctx, types.NamespacedName{Name: clusterName}, &clusterv1beta1.MemberCluster{})
		if errors.IsNotFound(err) {
			return nil
		}

		return err
	}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to delete member cluster")
}

func createPickAllCRPWithPolicySnapshot(crpName string, policySnapshotName string, policy *placementv1beta1.PlacementPolicy) {
	// Create a CRP of the PickAll placement type.
	crp := placementv1beta1.ClusterResourcePlacement{
		ObjectMeta: metav1.ObjectMeta{
			Name:       crpName,
			Finalizers: []string{customDeletionBlockerFinalizer},
		},
		Spec: placementv1beta1.PlacementSpec{
			ResourceSelectors: defaultResourceSelectors,
			Policy:            policy,
		},
	}
	Expect(hubClient.Create(ctx, &crp)).Should(Succeed(), "Failed to create CRP")

	crpGeneration := crp.Generation

	// Create the associated policy snapshot.
	policySnapshot := &placementv1beta1.ClusterSchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name: policySnapshotName,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: crpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(crpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).Should(Succeed(), "Failed to create policy snapshot")
}

func createPickAllRPWithPolicySnapshot(namespace, rpName, policySnapshotName string, policy *placementv1beta1.PlacementPolicy) {
	// Create a RP of the PickAll placement type.
	rp := placementv1beta1.ResourcePlacement{
		ObjectMeta: metav1.ObjectMeta{
			Name:       rpName,
			Namespace:  namespace,
			Finalizers: []string{customDeletionBlockerFinalizer},
		},
		Spec: placementv1beta1.PlacementSpec{
			ResourceSelectors: defaultResourceSelectors,
			Policy:            policy,
		},
	}
	Expect(hubClient.Create(ctx, &rp)).Should(Succeed(), "Failed to create RP")

	rpGeneration := rp.Generation

	// Create the associated policy snapshot.
	policySnapshot := &placementv1beta1.SchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name:      policySnapshotName,
			Namespace: namespace,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: rpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(rpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).Should(Succeed(), "Failed to create policy snapshot")
}

func updatePickAllCRPWithNewAffinity(crpName string, affinity *placementv1beta1.Affinity, oldPolicySnapshotName, newPolicySnapshotName string) {
	// Update the CRP.
	crp := &placementv1beta1.ClusterResourcePlacement{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: crpName}, crp)).To(Succeed(), "Failed to get CRP")

	policy := crp.Spec.Policy.DeepCopy()
	policy.Affinity = affinity
	crp.Spec.Policy = policy
	Expect(hubClient.Update(ctx, crp)).To(Succeed(), "Failed to update CRP")

	crpGeneration := crp.Generation

	// Mark the old policy snapshot as inactive.
	policySnapshot := &placementv1beta1.ClusterSchedulingPolicySnapshot{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: oldPolicySnapshotName}, policySnapshot)).To(Succeed(), "Failed to get policy snapshot")
	policySnapshot.Labels[placementv1beta1.IsLatestSnapshotLabel] = strconv.FormatBool(false)
	Expect(hubClient.Update(ctx, policySnapshot)).To(Succeed(), "Failed to update policy snapshot")

	// Create a new policy snapshot.
	policySnapshot = &placementv1beta1.ClusterSchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name: newPolicySnapshotName,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: crpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(crpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).To(Succeed(), "Failed to create policy snapshot")
}

func updatePickAllRPWithNewAffinity(namespace, rpName string, affinity *placementv1beta1.Affinity, oldPolicySnapshotName, newPolicySnapshotName string) {
	// Update the RP.
	rp := &placementv1beta1.ResourcePlacement{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: rpName}, rp)).To(Succeed(), "Failed to get RP")

	policy := rp.Spec.Policy.DeepCopy()
	policy.Affinity = affinity
	rp.Spec.Policy = policy
	Expect(hubClient.Update(ctx, rp)).To(Succeed(), "Failed to update RP")

	rpGeneration := rp.Generation

	// Mark the old policy snapshot as inactive.
	policySnapshot := &placementv1beta1.SchedulingPolicySnapshot{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: oldPolicySnapshotName, Namespace: namespace}, policySnapshot)).To(Succeed(), "Failed to get policy snapshot")
	policySnapshot.Labels[placementv1beta1.IsLatestSnapshotLabel] = strconv.FormatBool(false)
	Expect(hubClient.Update(ctx, policySnapshot)).To(Succeed(), "Failed to update policy snapshot")

	// Create a new policy snapshot.
	policySnapshot = &placementv1beta1.SchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name:      newPolicySnapshotName,
			Namespace: namespace,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: rpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation: strconv.FormatInt(rpGeneration, 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).To(Succeed(), "Failed to create policy snapshot")
}

func createPickNCRPWithPolicySnapshot(crpName string, policySnapshotName string, policy *placementv1beta1.PlacementPolicy) {
	// Create a CRP of the PickN placement type.
	crp := placementv1beta1.ClusterResourcePlacement{
		ObjectMeta: metav1.ObjectMeta{
			Name:       crpName,
			Finalizers: []string{customDeletionBlockerFinalizer},
		},
		Spec: placementv1beta1.PlacementSpec{
			ResourceSelectors: defaultResourceSelectors,
			Policy:            policy,
		},
	}
	Expect(hubClient.Create(ctx, &crp)).Should(Succeed(), "Failed to create CRP")

	crpGeneration := crp.Generation

	// Create the associated policy snapshot.
	policySnapshot := &placementv1beta1.ClusterSchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name: policySnapshotName,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: crpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation:    strconv.FormatInt(crpGeneration, 10),
				placementv1beta1.NumberOfClustersAnnotation: strconv.FormatInt(int64(*policy.NumberOfClusters), 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).Should(Succeed(), "Failed to create policy snapshot")
}

func createPickNRPWithPolicySnapshot(namespace, rpName string, policySnapshotName string, policy *placementv1beta1.PlacementPolicy) {
	// Create a RP of the PickN placement type.
	rp := placementv1beta1.ResourcePlacement{
		ObjectMeta: metav1.ObjectMeta{
			Name:       rpName,
			Namespace:  namespace,
			Finalizers: []string{customDeletionBlockerFinalizer},
		},
		Spec: placementv1beta1.PlacementSpec{
			ResourceSelectors: defaultResourceSelectors,
			Policy:            policy,
		},
	}
	Expect(hubClient.Create(ctx, &rp)).Should(Succeed(), "Failed to create CRP")

	rpGeneration := rp.Generation

	// Create the associated policy snapshot.
	policySnapshot := &placementv1beta1.SchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name:      policySnapshotName,
			Namespace: namespace,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: rpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation:    strconv.FormatInt(rpGeneration, 10),
				placementv1beta1.NumberOfClustersAnnotation: strconv.FormatInt(int64(*policy.NumberOfClusters), 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).Should(Succeed(), "Failed to create policy snapshot")
}

func updatePickNCRPWithNewAffinityAndTopologySpreadConstraints(
	crpName string,
	affinity *placementv1beta1.Affinity,
	topologySpreadConstraints []placementv1beta1.TopologySpreadConstraint,
	oldPolicySnapshotName, newPolicySnapshotName string,
) {
	// Update the CRP.
	crp := &placementv1beta1.ClusterResourcePlacement{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: crpName}, crp)).To(Succeed(), "Failed to get CRP")

	policy := crp.Spec.Policy.DeepCopy()
	numOfClusters := policy.NumberOfClusters
	policy.Affinity = affinity
	policy.TopologySpreadConstraints = topologySpreadConstraints
	crp.Spec.Policy = policy
	Expect(hubClient.Update(ctx, crp)).To(Succeed(), "Failed to update CRP")

	crpGeneration := crp.Generation

	// Mark the old policy snapshot as inactive.
	policySnapshot := &placementv1beta1.ClusterSchedulingPolicySnapshot{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: oldPolicySnapshotName}, policySnapshot)).To(Succeed(), "Failed to get policy snapshot")
	policySnapshot.Labels[placementv1beta1.IsLatestSnapshotLabel] = strconv.FormatBool(false)
	Expect(hubClient.Update(ctx, policySnapshot)).To(Succeed(), "Failed to update policy snapshot")

	// Create a new policy snapshot.
	policySnapshot = &placementv1beta1.ClusterSchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name: newPolicySnapshotName,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: crpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation:    strconv.FormatInt(crpGeneration, 10),
				placementv1beta1.NumberOfClustersAnnotation: strconv.FormatInt(int64(*numOfClusters), 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).To(Succeed(), "Failed to create policy snapshot")
}

func updatePickNCRPWithTolerations(crpName string, tolerations []placementv1beta1.Toleration, oldPolicySnapshotName, newPolicySnapshotName string) {
	crp := &placementv1beta1.ClusterResourcePlacement{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: crpName}, crp)).To(Succeed(), "Failed to get cluster resource placement")

	policy := crp.Spec.Policy.DeepCopy()
	policy.Tolerations = tolerations
	numOfClusters := policy.NumberOfClusters
	Expect(hubClient.Update(ctx, crp)).To(Succeed(), "Failed to update cluster resource placement")

	crpGeneration := crp.Generation

	// Mark the old policy snapshot as inactive.
	policySnapshot := &placementv1beta1.ClusterSchedulingPolicySnapshot{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: oldPolicySnapshotName}, policySnapshot)).To(Succeed(), "Failed to get policy snapshot")
	policySnapshot.Labels[placementv1beta1.IsLatestSnapshotLabel] = strconv.FormatBool(false)
	Expect(hubClient.Update(ctx, policySnapshot)).To(Succeed(), "Failed to update policy snapshot")

	// Create the associated policy snapshot.
	policySnapshot = &placementv1beta1.ClusterSchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name: newPolicySnapshotName,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: crpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation:    strconv.FormatInt(crpGeneration, 10),
				placementv1beta1.NumberOfClustersAnnotation: strconv.FormatInt(int64(*numOfClusters), 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).Should(Succeed(), "Failed to create policy snapshot")
}

func updatePickNRPWithTolerations(namespace, rpName string, tolerations []placementv1beta1.Toleration, oldPolicySnapshotName, newPolicySnapshotName string) {
	rp := &placementv1beta1.ResourcePlacement{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: rpName}, rp)).To(Succeed(), "Failed to get resource placement")

	policy := rp.Spec.Policy.DeepCopy()
	policy.Tolerations = tolerations
	numOfClusters := policy.NumberOfClusters
	Expect(hubClient.Update(ctx, rp)).To(Succeed(), "Failed to update resource placement")

	rpGeneration := rp.Generation

	// Mark the old policy snapshot as inactive.
	policySnapshot := &placementv1beta1.SchedulingPolicySnapshot{}
	Expect(hubClient.Get(ctx, types.NamespacedName{Name: oldPolicySnapshotName, Namespace: namespace}, policySnapshot)).To(Succeed(), "Failed to get policy snapshot")
	policySnapshot.Labels[placementv1beta1.IsLatestSnapshotLabel] = strconv.FormatBool(false)
	Expect(hubClient.Update(ctx, policySnapshot)).To(Succeed(), "Failed to update policy snapshot")

	// Create the associated policy snapshot.
	policySnapshot = &placementv1beta1.SchedulingPolicySnapshot{
		ObjectMeta: metav1.ObjectMeta{
			Name:      newPolicySnapshotName,
			Namespace: namespace,
			Labels: map[string]string{
				placementv1beta1.IsLatestSnapshotLabel:  strconv.FormatBool(true),
				placementv1beta1.PlacementTrackingLabel: rpName,
			},
			Annotations: map[string]string{
				placementv1beta1.CRPGenerationAnnotation:    strconv.FormatInt(rpGeneration, 10),
				placementv1beta1.NumberOfClustersAnnotation: strconv.FormatInt(int64(*numOfClusters), 10),
			},
		},
		Spec: placementv1beta1.SchedulingPolicySnapshotSpec{
			Policy:     policy,
			PolicyHash: []byte(policyHash),
		},
	}
	Expect(hubClient.Create(ctx, policySnapshot)).Should(Succeed(), "Failed to create policy snapshot")
}

func buildTaints(memberClusterNames []string) []clusterv1beta1.Taint {
	var labels map[string]string
	taints := make([]clusterv1beta1.Taint, len(memberClusterNames))
	for i, name := range memberClusterNames {
		if labelsByCluster[name] != nil {
			labels = labelsByCluster[name]
		} else {
			// for new member clusters added.
			labels = map[string]string{
				regionLabel: "unknown",
				envLabel:    "unknown",
			}
		}
		taints[i].Key = labels[regionLabel]
		taints[i].Value = labels[envLabel]
		taints[i].Effect = corev1.TaintEffectNoSchedule
	}
	return taints
}

func buildTolerations(memberClusterNames []string) []placementv1beta1.Toleration {
	var labels map[string]string
	tolerations := make([]placementv1beta1.Toleration, len(memberClusterNames))
	for i, name := range memberClusterNames {
		if labelsByCluster[name] != nil {
			labels = labelsByCluster[name]
		} else {
			// for new member clusters added.
			labels = map[string]string{
				regionLabel: "unknown",
				envLabel:    "unknown",
			}
		}
		tolerations[i].Key = labels[regionLabel]
		tolerations[i].Operator = corev1.TolerationOpEqual
		tolerations[i].Value = labels[envLabel]
		tolerations[i].Effect = corev1.TaintEffectNoSchedule
	}
	return tolerations
}

func addTaintsToMemberClusters(memberClusterNames []string, taints []clusterv1beta1.Taint) {
	for i, clusterName := range memberClusterNames {
		var mc clusterv1beta1.MemberCluster
		Expect(hubClient.Get(ctx, types.NamespacedName{Name: clusterName}, &mc)).Should(Succeed(), "Failed to get member cluster")
		mc.Spec.Taints = []clusterv1beta1.Taint{taints[i]}
		Expect(hubClient.Update(ctx, &mc)).Should(Succeed(), "Failed to update member cluster")
	}
}

func removeTaintsFromMemberClusters(memberClusterNames []string) {
	for _, clusterName := range memberClusterNames {
		var mc clusterv1beta1.MemberCluster
		Expect(hubClient.Get(ctx, types.NamespacedName{Name: clusterName}, &mc)).Should(Succeed(), "Failed to get member cluster")
		mc.Spec.Taints = nil
		Expect(hubClient.Update(ctx, &mc)).Should(Succeed(), "Failed to update member cluster")
	}
}

func resetClusterPropertiesFor(clusterName string) {
	now := metav1.Now()
	expectedPropertyStatus := propertiesByCluster[clusterName]

	// Reset the agent status.
	Eventually(func() error {
		memberCluster := &clusterv1beta1.MemberCluster{}
		if err := hubClient.Get(ctx, types.NamespacedName{Name: clusterName}, memberCluster); err != nil {
			return fmt.Errorf("failed to get member cluster: %w", err)
		}

		properties := map[clusterv1beta1.PropertyName]clusterv1beta1.PropertyValue{}
		for pn, pv := range expectedPropertyStatus.Properties {
			pvc := pv.DeepCopy()
			pvc.ObservationTime = now
			properties[pn] = *pvc
		}
		memberCluster.Status.Properties = properties

		memberCluster.Status.ResourceUsage = *expectedPropertyStatus.ResourceUsage.DeepCopy()
		memberCluster.Status.ResourceUsage.ObservationTime = now

		if err := hubClient.Status().Update(ctx, memberCluster); err != nil {
			return fmt.Errorf("failed to update member cluster status: %w", err)
		}
		return nil
	}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to reset cluster properties")
}

func listBindings(placementKey types.NamespacedName) (placementv1beta1.BindingObjList, error) {
	var bindingList placementv1beta1.BindingObjList
	labelSelector := labels.SelectorFromSet(labels.Set{placementv1beta1.PlacementTrackingLabel: placementKey.Name})
	listOptions := &client.ListOptions{LabelSelector: labelSelector}

	if placementKey.Namespace == "" {
		// List ClusterResourceBindings.
		bindingList = &placementv1beta1.ClusterResourceBindingList{}
	} else {
		// List ResourceBindings.
		bindingList = &placementv1beta1.ResourceBindingList{}
		listOptions.Namespace = placementKey.Namespace
	}

	if err := hubClient.List(ctx, bindingList, listOptions); err != nil {
		return nil, err
	}
	return bindingList, nil
}

func getSchedulingPolicySnapshot(policySnapshotKey types.NamespacedName) (placementv1beta1.PolicySnapshotObj, error) {
	// Get the policy snapshot.
	var policySnapshot placementv1beta1.PolicySnapshotObj
	if policySnapshotKey.Namespace == "" {
		// Get ClusterSchedulingPolicySnapshot.
		policySnapshot = &placementv1beta1.ClusterSchedulingPolicySnapshot{}
	} else {
		// Get SchedulingPolicySnapshot.
		policySnapshot = &placementv1beta1.SchedulingPolicySnapshot{}
	}
	if err := hubClient.Get(ctx, types.NamespacedName{Name: policySnapshotKey.Name, Namespace: policySnapshotKey.Namespace}, policySnapshot); err != nil {
		return nil, err
	}
	return policySnapshot, nil
}
