/*
Copyright 2021 The Karmada 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 helper

import (
	"encoding/json"

	"k8s.io/klog/v2"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client"
	mcsv1alpha1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"

	policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
	"github.com/karmada-io/karmada/pkg/util"
)

// SetDefaultSpreadConstraints set default spread constraints if both 'SpreadByField' and 'SpreadByLabel' not set.
func SetDefaultSpreadConstraints(spreadConstraints []policyv1alpha1.SpreadConstraint) {
	for i := range spreadConstraints {
		if len(spreadConstraints[i].SpreadByLabel) == 0 && len(spreadConstraints[i].SpreadByField) == 0 {
			klog.Infof("Setting default SpreadByField with %s", policyv1alpha1.SpreadByFieldCluster)
			spreadConstraints[i].SpreadByField = policyv1alpha1.SpreadByFieldCluster
		}

		if spreadConstraints[i].MinGroups == 0 {
			klog.Infof("Setting default MinGroups to 1")
			spreadConstraints[i].MinGroups = 1
		}
	}
}

// IsDependentOverridesPresent checks if a PropagationPolicy's dependent OverridePolicy all exist.
func IsDependentOverridesPresent(c client.Client, policy *policyv1alpha1.PropagationPolicy) (bool, error) {
	ns := policy.Namespace
	for _, override := range policy.Spec.DependentOverrides {
		exist, err := IsOverridePolicyExist(c, ns, override)
		if err != nil {
			return false, err
		}
		if !exist {
			return false, nil
		}
	}

	return true, nil
}

// IsDependentClusterOverridesPresent checks if a ClusterPropagationPolicy's dependent ClusterOverridePolicy all exist.
func IsDependentClusterOverridesPresent(c client.Client, policy *policyv1alpha1.ClusterPropagationPolicy) (bool, error) {
	for _, override := range policy.Spec.DependentOverrides {
		exist, err := IsClusterOverridePolicyExist(c, override)
		if err != nil {
			return false, err
		}
		if !exist {
			return false, nil
		}
	}

	return true, nil
}

// ContainsServiceImport Check whether the ResourceSelectors of the policy contain ResourceSelector, and its Kind is ServiceImport.
func ContainsServiceImport(resourceSelectors []policyv1alpha1.ResourceSelector) bool {
	for _, resourceSelector := range resourceSelectors {
		if resourceSelector.Kind != util.ServiceImportKind {
			continue
		}
		if resourceSelector.APIVersion != mcsv1alpha1.GroupVersion.String() {
			continue
		}
		return true
	}
	return false
}

// IsReplicaDynamicDivided checks if a PropagationPolicy schedules replicas as dynamic.
func IsReplicaDynamicDivided(placement *policyv1alpha1.Placement) bool {
	if placement.ReplicaSchedulingType() != policyv1alpha1.ReplicaSchedulingTypeDivided {
		return false
	}

	strategy := placement.ReplicaScheduling
	switch strategy.ReplicaDivisionPreference {
	case policyv1alpha1.ReplicaDivisionPreferenceWeighted:
		return strategy.WeightPreference != nil && len(strategy.WeightPreference.DynamicWeight) != 0
	case policyv1alpha1.ReplicaDivisionPreferenceAggregated:
		return true
	default:
		return false
	}
}

// GetAppliedPlacement will get applied placement from annotations.
func GetAppliedPlacement(annotations map[string]string) (*policyv1alpha1.Placement, error) {
	appliedPlacement := util.GetLabelValue(annotations, util.PolicyPlacementAnnotation)
	if len(appliedPlacement) == 0 {
		return nil, nil
	}
	placement := &policyv1alpha1.Placement{}
	if err := json.Unmarshal([]byte(appliedPlacement), placement); err != nil {
		return nil, err
	}
	return placement, nil
}

// SetReplicaDivisionPreferenceWeighted sets the default value of ReplicaDivisionPreference to Weighted
func SetReplicaDivisionPreferenceWeighted(placement *policyv1alpha1.Placement) {
	if placement.ReplicaSchedulingType() != policyv1alpha1.ReplicaSchedulingTypeDivided || placement.ReplicaScheduling.ReplicaDivisionPreference != "" {
		return
	}
	placement.ReplicaScheduling.ReplicaDivisionPreference = policyv1alpha1.ReplicaDivisionPreferenceWeighted
}

// SetDefaultGracePeriodSeconds sets the default value of GracePeriodSeconds in ApplicationFailoverBehavior
func SetDefaultGracePeriodSeconds(behavior *policyv1alpha1.ApplicationFailoverBehavior) {
	if behavior.PurgeMode == policyv1alpha1.Graciously && behavior.GracePeriodSeconds == nil {
		behavior.GracePeriodSeconds = ptr.To[int32](600)
	}
}
