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

package tenant

import (
	"context"
	"fmt"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"

	capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
	"github.com/projectcapsule/capsule/pkg/api/meta"
	"github.com/projectcapsule/capsule/pkg/api/rules"
	"github.com/projectcapsule/capsule/pkg/runtime/sanitize"
	"github.com/projectcapsule/capsule/pkg/runtime/selectors"
	"github.com/projectcapsule/capsule/pkg/template"
)

func GetManagedRuleStatus(
	ctx context.Context,
	c client.Reader,
	ns *corev1.Namespace,
) (*capsulev1beta2.RuleStatus, error) {
	obj := &capsulev1beta2.RuleStatus{}

	err := c.Get(ctx, types.NamespacedName{Name: meta.NameForManagedRuleStatus(), Namespace: ns.GetName()}, obj)
	if err != nil {
		return nil, err
	}

	return obj, err
}

// BuildNamespaceRuleBodyStatus returns the aggregated rule bodies that apply to ns.
// - Rules with nil NamespaceSelector match all namespaces.
// - Matching rules are returned in the order they appear in tnt.Spec.Rules.
// - Order is important because registry/QoS evaluation uses "later allow/deny wins" semantics.
func BuildNamespaceRuleBodyStatus(
	scheme *runtime.Scheme,
	ns *corev1.Namespace,
	tnt *capsulev1beta2.Tenant,
) ([]*rules.NamespaceRuleBodyNamespace, error) {
	if tnt == nil || ns == nil {
		return nil, nil
	}

	nsLabels := labels.Set{}
	if ns.Labels != nil {
		nsLabels = labels.Set(ns.Labels)
	}

	templateOptions := sanitize.DefaultSanitizeOptions()
	templateOptions.StripStatus = false

	templateContext, err := NewTenantNamespaceContext(tnt, ns, scheme, templateOptions)
	if err != nil {
		return nil, fmt.Errorf("build namespace rule template context: %w", err)
	}

	selected := make([]*rules.NamespaceRuleBodyNamespace, 0, len(tnt.Spec.Rules))

	for i, rule := range tnt.Spec.Rules {
		if rule == nil {
			continue
		}

		if rule.NamespaceSelector != nil {
			matches, err := selectors.MatchesSelector(nsLabels, *rule.NamespaceSelector)
			if err != nil {
				return nil, fmt.Errorf("invalid namespaceSelector in rules[%d]: %w", i, err)
			}

			if !matches {
				continue
			}
		}

		body := rule.NamespaceRuleBodyNamespace
		if body == nil || body.Enforce == nil {
			continue
		}

		statusBody := body.DeepCopy()
		// Quotas are Tenant-level inputs used to generate cluster-scoped
		// GlobalResourceQuotas. They are not evaluated from per-namespace
		// RuleStatus objects and must not be projected into them.
		statusBody.Quota = nil
		selected = append(selected, statusBody)
	}

	rendered, err := template.RenderNamespaceRuleBodies(
		templateContext,
		template.MissingKeyError,
		selected,
	)
	if err != nil {
		return nil, fmt.Errorf("render namespace rule bodies: %w", err)
	}

	out := make([]*rules.NamespaceRuleBodyNamespace, 0, len(rendered))

	for _, body := range rendered {
		if body == nil || body.Enforce == nil {
			continue
		}

		body.Quota = nil
		out = append(out, body)
	}

	return out, nil
}
