// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0

package meta

import (
	"context"
	"maps"
	"strings"
	"time"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/util/retry"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
	ReleaseAnnotation        = "projectcapsule.dev/release"
	ReleaseAnnotationTrigger = "true"

	ReconcileAnnotation = "reconcile.projectcapsule.dev/requestedAt"

	AvailableIngressClassesAnnotation       = "capsule.clastix.io/ingress-classes"
	AvailableIngressClassesRegexpAnnotation = "capsule.clastix.io/ingress-classes-regexp"
	AvailableStorageClassesAnnotation       = "capsule.clastix.io/storage-classes"
	AvailableStorageClassesRegexpAnnotation = "capsule.clastix.io/storage-classes-regexp"
	AllowedRegistriesAnnotation             = "capsule.clastix.io/allowed-registries"
	AllowedRegistriesRegexpAnnotation       = "capsule.clastix.io/allowed-registries-regexp"

	ForbiddenNamespaceLabelsAnnotation            = "capsule.clastix.io/forbidden-namespace-labels"
	ForbiddenNamespaceLabelsRegexpAnnotation      = "capsule.clastix.io/forbidden-namespace-labels-regexp"
	ForbiddenNamespaceAnnotationsAnnotation       = "capsule.clastix.io/forbidden-namespace-annotations"
	ForbiddenNamespaceAnnotationsRegexpAnnotation = "capsule.clastix.io/forbidden-namespace-annotations-regexp"
	ProtectedTenantAnnotation                     = "capsule.clastix.io/protected"

	ResourceQuotaAnnotationPrefix = "quota.resources.capsule.clastix.io"
	ResourceUsedAnnotationPrefix  = "used.resources.capsule.clastix.io"

	// Audit Annotations.
	AuditRequestUID    = "audit.projectcapsule.dev/request-uid"
	AuditUsername      = "audit.projectcapsule.dev/username"
	AuditRuleSetName   = "audit.projectcapsule.dev/rule-set"
	AuditRuleSetAction = "audit.projectcapsule.dev/rule-action"
)

func ReleaseAnnotationTriggers(obj client.Object) bool {
	return annotationTriggers(obj, ReleaseAnnotation, ReleaseAnnotationTrigger)
}

func ReleaseAnnotationRemove(obj client.Object) {
	annotationRemove(obj, ReleaseAnnotation)
}

func TriggerRequestReconcileAnnotation(
	ctx context.Context,
	c client.Client,
	gvk schema.GroupVersionKind,
	key types.NamespacedName,
) error {
	return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
		obj := &unstructured.Unstructured{}
		obj.SetGroupVersionKind(gvk)

		if err := c.Get(ctx, key, obj); err != nil {
			return err
		}

		base := obj.DeepCopy()

		annotations := obj.GetAnnotations()
		if annotations == nil {
			annotations = map[string]string{}
		}

		annotations[ReconcileAnnotation] = time.Now().UTC().Format(time.RFC3339Nano)

		obj.SetAnnotations(annotations)

		return c.Patch(ctx, obj, client.MergeFrom(base))
	})
}

func RemoveReconcileTriggerAnnotation(
	obj client.Object,
) {
	annotations := obj.GetAnnotations()
	if _, ok := annotations[ReconcileAnnotation]; !ok {
		return
	}

	annotations = maps.Clone(annotations)
	delete(annotations, ReconcileAnnotation)

	if len(annotations) == 0 {
		obj.SetAnnotations(nil)
	} else {
		obj.SetAnnotations(annotations)
	}
}

func annotationRemove(obj client.Object, anno string) {
	annotations := obj.GetAnnotations()

	if _, ok := annotations[anno]; ok {
		delete(annotations, anno)

		obj.SetAnnotations(annotations)
	}
}

func annotationTriggers(obj client.Object, anno string, trigger string) bool {
	annotations := obj.GetAnnotations()

	if val, ok := annotations[anno]; ok {
		if strings.ToLower(val) == trigger {
			return true
		}
	}

	return false
}
