package overridemanager

import (
	"strings"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

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

func applyLabelsOverriders(rawObj *unstructured.Unstructured, labelOverriders []policyv1alpha1.LabelAnnotationOverrider) error {
	return applyLabelAnnotationOverriders(rawObj, labelOverriders, "metadata", "labels")
}

func applyAnnotationsOverriders(rawObj *unstructured.Unstructured, annotationOverriders []policyv1alpha1.LabelAnnotationOverrider) error {
	return applyLabelAnnotationOverriders(rawObj, annotationOverriders, "metadata", "annotations")
}

func applyLabelAnnotationOverriders(rawObj *unstructured.Unstructured, labelAnnotationOverriders []policyv1alpha1.LabelAnnotationOverrider, path ...string) error {
	for index := range labelAnnotationOverriders {
		patches := buildLabelAnnotationOverriderPatches(rawObj, labelAnnotationOverriders[index], path)
		if len(patches) == 0 {
			continue
		}
		if err := applyJSONPatch(rawObj, patches); err != nil {
			return err
		}
	}
	return nil
}

func buildLabelAnnotationOverriderPatches(rawObj *unstructured.Unstructured, overrider policyv1alpha1.LabelAnnotationOverrider, path []string) []overrideOption {
	patches := make([]overrideOption, 0)
	for key, value := range overrider.Value {
		switch overrider.Operator {
		case policyv1alpha1.OverriderOpRemove, policyv1alpha1.OverriderOpReplace:
			match, _, _ := unstructured.NestedStringMap(rawObj.Object, path...)
			if _, exist := match[key]; !exist {
				continue
			}
		case policyv1alpha1.OverriderOpAdd:
			_, exist, _ := unstructured.NestedStringMap(rawObj.Object, path...)
			if exist {
				break
			}
			if err := unstructured.SetNestedStringMap(rawObj.Object, map[string]string{}, path...); err != nil {
				continue
			}
		}
		// If the key contains '/', we must replace(escape) it to '~1' according to the
		// rule of jsonpath identifying a specific value.
		// See https://jsonpatch.com/#json-pointer for more details.
		// Note: here don't replace '~' because it is not a valid character for
		// both annotations and labels, the key with '~' should be prevented at
		// the validation phase.
		key = strings.ReplaceAll(key, "/", "~1")
		patches = append(patches, overrideOption{
			Op:    string(overrider.Operator),
			Path:  "/" + strings.Join(append(path, key), "/"),
			Value: value,
		})
	}
	return patches
}
