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

package ruleengine

import (
	"strings"
	"testing"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"

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

func TestValidateRuleStatusBodyWorkloadResources(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name    string
		body    *rules.NamespaceRuleBodyNamespace
		wantErr string
	}{
		{
			name: "valid",
			body: workloadResourceRuleForValidation(
				[]rules.WorkloadValidationTarget{rules.ValidateContainers},
				rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyDefault, Value: validationQuantity("1Gi")},
				rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
			),
		},
		{
			name: "omitted target permits container-only resource names",
			body: workloadResourceRuleForValidationWithName(
				nil,
				corev1.ResourceEphemeralStorage,
				rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
				rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
			),
		},
		{
			name: "ratio below one",
			body: workloadResourceRuleForValidation(
				nil,
				rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
				rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("0.5")},
			),
			wantErr: "Ratio must be greater than or equal to 1",
		},
		{
			name: "ratio missing value",
			body: workloadResourceRuleForValidation(
				nil,
				rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
				rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio},
			),
			wantErr: "Ratio requires a value",
		},
		{
			name: "unsupported target",
			body: workloadResourceRuleForValidation(
				[]rules.WorkloadValidationTarget{rules.ValidateEphemeralContainers},
				rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
				rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
			),
			wantErr: "does not support resource policies",
		},
		{
			name: "pod-level ephemeral storage",
			body: workloadResourceRuleForValidation(
				[]rules.WorkloadValidationTarget{rules.ValidatePod},
				rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
				rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
			),
			wantErr: "is not supported by pod-level resources",
		},
		{
			name: "ratio request removed",
			body: workloadResourceRuleForValidation(
				nil,
				rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyRemove},
				rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
			),
			wantErr: "requires a request which is removed",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			err := ValidateRuleStatusBody(nil, []*rules.NamespaceRuleBodyNamespace{tt.body})
			if tt.wantErr == "" {
				if err != nil {
					t.Fatalf("ValidateRuleStatusBody() error = %v", err)
				}

				return
			}

			if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
				t.Fatalf("ValidateRuleStatusBody() error = %v, want containing %q", err, tt.wantErr)
			}
		})
	}
}

func workloadResourceRuleForValidation(
	targets []rules.WorkloadValidationTarget,
	requestPolicy rules.WorkloadResourceRequestPolicy,
	limitPolicy rules.WorkloadResourceLimitPolicy,
) *rules.NamespaceRuleBodyNamespace {
	name := corev1.ResourceMemory
	if len(targets) == 1 && targets[0] == rules.ValidatePod {
		name = corev1.ResourceEphemeralStorage
	}

	return workloadResourceRuleForValidationWithName(targets, name, requestPolicy, limitPolicy)
}

func workloadResourceRuleForValidationWithName(
	targets []rules.WorkloadValidationTarget,
	name corev1.ResourceName,
	requestPolicy rules.WorkloadResourceRequestPolicy,
	limitPolicy rules.WorkloadResourceLimitPolicy,
) *rules.NamespaceRuleBodyNamespace {

	return &rules.NamespaceRuleBodyNamespace{
		Enforce: &rules.NamespaceRuleEnforceBody{
			Workloads: rules.NamespaceRuleEnforceWorkloadsBody{
				Targets: targets,
				Resources: &rules.WorkloadResourceRules{
					Requests: map[corev1.ResourceName]rules.WorkloadResourceRequestPolicy{name: requestPolicy},
					Limits:   map[corev1.ResourceName]rules.WorkloadResourceLimitPolicy{name: limitPolicy},
				},
			},
		},
	}
}

func validationQuantity(value string) *resource.Quantity {
	quantity := resource.MustParse(value)

	return &quantity
}
