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

package admission

import (
	"errors"
	"strings"

	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"

	"github.com/projectcapsule/capsule/pkg/api/meta"
)

// +kubebuilder:object:generate=true
// +kubebuilder:validation:XValidation:rule="has(self.client.url) != has(self.client.service)",message="client must configure exactly one of url or service"
type DynamicAdmissionConfig struct {
	// Name the Admission Webhook
	Name meta.RFC1123Name `json:"name,omitempty"`
	// Labels added to the Admission Webhook
	// +optional
	Labels map[string]string `json:"labels,omitempty"`
	// Annotations added to the Admission Webhook
	// +optional
	Annotations map[string]string `json:"annotations,omitempty"`
	// Client defines how the Kubernetes API server reaches the admission webhook.
	// Exactly one of URL or Service must be configured.
	Client *admissionregistrationv1.WebhookClientConfig `json:"client"`
}

// ValidateWebhookClientConfig checks the invariant required by the Kubernetes
// admissionregistration API before a dynamic webhook object is constructed.
func ValidateWebhookClientConfig(client *admissionregistrationv1.WebhookClientConfig) error {
	if client == nil {
		return errors.New("webhook client config is required")
	}

	hasURL := client.URL != nil

	hasService := client.Service != nil
	if hasURL == hasService {
		return errors.New("webhook client config must configure exactly one of url or service")
	}

	return nil
}

func DynamicWebhookURL(baseURL *string, webhookPath string) *string {
	cleanPath := normalizePath(webhookPath)
	if cleanPath == "" {
		if baseURL == nil || *baseURL == "" {
			return nil
		}

		u := *baseURL

		return &u
	}

	if baseURL == nil || *baseURL == "" {
		u := cleanPath

		return &u
	}

	base := strings.TrimRight(*baseURL, "/")

	if base == strings.TrimRight(cleanPath, "/") {
		u := cleanPath

		return &u
	}

	if strings.HasSuffix(base, cleanPath) {
		u := base

		return &u
	}

	u := base + cleanPath

	return &u
}

func DynamicClientWithPath(
	in admissionregistrationv1.WebhookClientConfig,
	webhookPath string,
) admissionregistrationv1.WebhookClientConfig {
	out := in

	if out.URL != nil {
		out.URL = DynamicWebhookURL(out.URL, webhookPath)

		return out
	}

	cleanPath := normalizePath(webhookPath)
	if cleanPath == "" {
		return out
	}

	if out.Service != nil {
		svc := *out.Service
		svc.Path = &cleanPath
		out.Service = &svc
	}

	return out
}
