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

package v1beta2

import (
	k8stypes "k8s.io/apimachinery/pkg/types"

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

// +kubebuilder:validation:Enum=Cordoned;Active;Terminating
type tenantState string

const (
	TenantStateActive      tenantState = "Active"
	TenantStateCordoned    tenantState = "Cordoned"
	TenantStateTerminating tenantState = "Terminating"
)

// Returns the observed state of the Tenant.
type TenantStatus struct {
	// Allowed Cluster Objects within Tenant
	TenantAvailableStatus `json:",inline"`

	// ObservedGeneration is the most recent generation the controller has observed.
	// +optional
	ObservedGeneration int64 `json:"observedGeneration,omitempty"`

	// Collected owners for this tenant
	Owners rbac.OwnerStatusListSpec `json:"owners,omitempty"`
	// Promoted ServiceAccounts across the Tenant
	Promotions rbac.PromotionStatusListSpec `json:"promotions,omitempty"`
	// +kubebuilder:default=Active
	// The operational state of the Tenant. Possible values are "Active", "Cordoned" or "Terminating".
	State tenantState `json:"state"`
	// How many namespaces are assigned to the Tenant.
	Size uint `json:"size"`
	// List of namespaces assigned to the Tenant. (Deprecated)
	Namespaces []string `json:"namespaces,omitempty"`
	// Tracks state for the namespaces associated with this tenant
	Spaces []*TenantStatusNamespaceItem `json:"spaces,omitempty"`
	// Tenant Condition
	Conditions meta.ConditionList `json:"conditions"`
}

type TenantStatusNamespaceItem struct {
	// Conditions
	Conditions meta.ConditionList `json:"conditions"`
	// Namespace Name
	Name string `json:"name"`
	// Namespace UID
	UID k8stypes.UID `json:"uid,omitempty"`
	// Managed Metadata
	Metadata *TenantStatusNamespaceMetadata `json:"metadata,omitempty"`
	// Managed Metadata
	//+optional
	Enforce TenantStatusNamespaceEnforcement `json:"enforce,omitzero"`
}

// RuleStatus contains the accumulated rules applying to namespace it's deployed in.
// +kubebuilder:object:generate=true
type TenantStatusRuleStatusItem struct {
	// Promotions originating from this namespace
	Promotions rbac.OwnerStatusListSpec `json:"promotions,omitempty"`

	// Target Namespaces for this rule
	TargetNamespaces []meta.RFC1123SubdomainName `json:"namespaces,omitempty"`
}

type TenantStatusNamespaceEnforcement struct {
	// Registries which are allowed within this namespace
	Registries []rules.OCIRegistry `json:"registry,omitempty"`
}

type TenantStatusNamespaceMetadata struct {
	// Managed Labels
	Labels map[string]string `json:"labels,omitempty"`
	// Managed Annotations
	Annotations map[string]string `json:"annotations,omitempty"`
}

type TenantAvailableStatus struct {
	// Available Class Types within Tenant
	// +optional
	Classes TenantAvailableClassesStatus `json:"classes,omitzero"`
}

type TenantAvailableClassesStatus struct {
	// Available Storageclasses (Only collected if any matching condition is specified)
	StorageClasses []string `json:"storage,omitempty"`
	// Available PriorityClasses
	PriorityClasses []string `json:"priority,omitempty"`
	// Available StorageClasses
	RuntimeClasses []string `json:"runtime,omitempty"`
	// Available GatewayClasses
	GatewayClasses []string `json:"gateway,omitempty"`
	// Available DeviceClasses
	DeviceClasses []string `json:"device,omitempty"`
}

func (ms *TenantStatus) GetInstance(stat *TenantStatusNamespaceItem) *TenantStatusNamespaceItem {
	for _, source := range ms.Spaces {
		if ms.instancequal(source, stat) {
			return source
		}
	}

	return nil
}

func (ms *TenantStatus) UpdateInstance(stat *TenantStatusNamespaceItem) {
	// Check if the tenant is already present in the status
	for i, source := range ms.Spaces {
		if ms.instancequal(source, stat) {
			ms.Spaces[i] = stat

			return
		}
	}

	ms.Spaces = append(ms.Spaces, stat)
}

func (ms *TenantStatus) RemoveInstance(stat *TenantStatusNamespaceItem) {
	// Filter out the datasource with given UID
	filter := []*TenantStatusNamespaceItem{}

	for _, source := range ms.Spaces {
		if !ms.instancequal(source, stat) {
			filter = append(filter, source)
		}
	}

	ms.Spaces = filter
}

func (ms *TenantStatus) instancequal(a, b *TenantStatusNamespaceItem) bool {
	return a.Name == b.Name
}
