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

package validation

import (
	"context"
	"regexp"

	corev1 "k8s.io/api/core/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

	capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
	ad "github.com/projectcapsule/capsule/pkg/runtime/admission"
	"github.com/projectcapsule/capsule/pkg/runtime/events"
	"github.com/projectcapsule/capsule/pkg/runtime/handlers"
	"github.com/projectcapsule/capsule/pkg/users"
)

type requiredMetadataHandler struct{}

func RequiredMetadataHandler() handlers.TypedHandlerWithTenantUser[*corev1.Namespace] {
	return &requiredMetadataHandler{}
}

//nolint:staticcheck
func (h *requiredMetadataHandler) OnCreate(
	_ client.Client,
	_ client.Reader,
	_ users.AdmissionUser,
	ns *corev1.Namespace,
	_ admission.Decoder,
	_ events.EventRecorder,
	tnt *capsulev1beta2.Tenant,
) handlers.Func {
	return func(_ context.Context, _ admission.Request) *admission.Response {
		no := tnt.Spec.NamespaceOptions
		if no == nil || no.RequiredMetadata == nil {
			return nil
		}

		rm := no.RequiredMetadata

		if resp := validateRequiredMapCreate(
			"label",
			rm.Labels,
			ns.GetLabels(),
		); resp != nil {
			return resp
		}

		if resp := validateRequiredMapCreate(
			"annotation",
			rm.Annotations,
			ns.GetAnnotations(),
		); resp != nil {
			return resp
		}

		return nil
	}
}

//nolint:staticcheck
func (h *requiredMetadataHandler) OnUpdate(
	_ client.Client,
	_ client.Reader,
	_ users.AdmissionUser,
	newNs *corev1.Namespace,
	oldNs *corev1.Namespace,
	_ admission.Decoder,
	_ events.EventRecorder,
	tnt *capsulev1beta2.Tenant,
) handlers.Func {
	return func(_ context.Context, _ admission.Request) *admission.Response {
		no := tnt.Spec.NamespaceOptions
		if no == nil || no.RequiredMetadata == nil {
			return nil
		}

		rm := no.RequiredMetadata

		if resp := validateRequiredMapUpdate(
			"label",
			rm.Labels,
			newNs.GetLabels(),
			oldNs.GetLabels(),
		); resp != nil {
			return resp
		}

		if resp := validateRequiredMapUpdate(
			"annotation",
			rm.Annotations,
			newNs.GetAnnotations(),
			oldNs.GetAnnotations(),
		); resp != nil {
			return resp
		}

		return nil
	}
}

func (h *requiredMetadataHandler) OnDelete(
	client.Client,
	client.Reader,
	users.AdmissionUser,
	*corev1.Namespace,
	admission.Decoder,
	events.EventRecorder,
	*capsulev1beta2.Tenant,
) handlers.Func {
	return func(context.Context, admission.Request) *admission.Response { return nil }
}

func validateRequiredMapCreate(kind string, required map[string]string, actual map[string]string) *admission.Response {
	for key, exp := range required {
		val, ok := actual[key]
		if !ok {
			return ad.Denyf("required %s %q not present", kind, key)
		}

		re, reErr := regexp.Compile(exp)
		if reErr != nil {
			return ad.Denyf("invalid required %s regex for %q: %q: %v", kind, key, exp, reErr)
		}

		if !re.MatchString(val) {
			return ad.Denyf("required %s %q value %q does not match regex %q", kind, key, val, exp)
		}
	}

	return nil
}

func validateRequiredMapUpdate(kind string, required map[string]string, newMap, oldMap map[string]string) *admission.Response {
	mismatchKind := kind
	if kind == "label" {
		mismatchKind = "annotation"
	}

	for key, exp := range required {
		valNew, newOK := newMap[key]
		valOld, oldOK := oldMap[key]

		if newOK == oldOK && (!newOK || valNew == valOld) {
			continue
		}

		if !newOK {
			return ad.Denyf("required %s %q not present", kind, key)
		}

		re, reErr := regexp.Compile(exp)
		if reErr != nil {
			return ad.Denyf("invalid required %s regex for %q: %q: %v", kind, key, exp, reErr)
		}

		if !re.MatchString(valNew) {
			return ad.Denyf("required %s %q value %q does not match regex %q", mismatchKind, key, valNew, exp)
		}
	}

	return nil
}
