/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	templatev1alpha1 "github.com/sandbox0-ai/sandbox0/manager/pkg/apis/sandbox0/v1alpha1"
	"github.com/sandbox0-ai/sandbox0/pkg/rediscache"
)

// DatabaseType defines the type of database
// +kubebuilder:validation:Enum=builtin;external
type DatabaseType string

const (
	DatabaseTypeBuiltin  DatabaseType = "builtin"
	DatabaseTypeExternal DatabaseType = "external"
)

// StorageType defines the type of storage backend
// +kubebuilder:validation:Enum=builtin;s3;oss;gcs
type StorageType string

const (
	StorageTypeBuiltin StorageType = "builtin"
	StorageTypeS3      StorageType = "s3"
	StorageTypeOSS     StorageType = "oss"
	StorageTypeGCS     StorageType = "gcs"
)

// RedisType defines the type of Redis backend.
// +kubebuilder:validation:Enum=builtin;external
type RedisType string

const (
	RedisTypeBuiltin  RedisType = "builtin"
	RedisTypeExternal RedisType = "external"
)

// CredentialVaultType defines the type of Vault-compatible credential store.
// +kubebuilder:validation:Enum=builtin;external
type CredentialVaultType string

const (
	CredentialVaultTypeBuiltin  CredentialVaultType = "builtin"
	CredentialVaultTypeExternal CredentialVaultType = "external"
)

// RegistryProvider defines the registry provider type.
// +kubebuilder:validation:Enum=builtin;aws;gcp;azure;aliyun;harbor
type RegistryProvider string

// ObservabilityTraceExporter defines the trace exporter configured through
// standard OpenTelemetry environment variables.
// +kubebuilder:validation:Enum=otlp;stdout;noop
type ObservabilityTraceExporter string

// ObservabilityBackendType defines the observability export integration mode.
// +kubebuilder:validation:Enum=disabled;external
type ObservabilityBackendType string

// ObservabilityExternalMode defines how sandbox0 exports telemetry to external
// observability infrastructure.
// +kubebuilder:validation:Enum=managedCollector;existingCollector
type ObservabilityExternalMode string

// BuiltinStatefulResourcePolicy controls how builtin stateful resources are
// handled when a builtin component is disabled or switched to an external
// provider.
// +kubebuilder:validation:Enum=Retain;Delete
type BuiltinStatefulResourcePolicy string

const (
	RegistryProviderBuiltin RegistryProvider = "builtin"
	RegistryProviderAWS     RegistryProvider = "aws"
	RegistryProviderGCP     RegistryProvider = "gcp"
	RegistryProviderAzure   RegistryProvider = "azure"
	RegistryProviderAliyun  RegistryProvider = "aliyun"
	RegistryProviderHarbor  RegistryProvider = "harbor"
)

const (
	ObservabilityTraceExporterOTLP   ObservabilityTraceExporter = "otlp"
	ObservabilityTraceExporterStdout ObservabilityTraceExporter = "stdout"
	ObservabilityTraceExporterNoop   ObservabilityTraceExporter = "noop"
)

const (
	ObservabilityBackendTypeDisabled ObservabilityBackendType = "disabled"
	ObservabilityBackendTypeExternal ObservabilityBackendType = "external"
)

const (
	ObservabilityExternalModeManagedCollector  ObservabilityExternalMode = "managedCollector"
	ObservabilityExternalModeExistingCollector ObservabilityExternalMode = "existingCollector"
)

const (
	BuiltinStatefulResourcePolicyRetain BuiltinStatefulResourcePolicy = "Retain"
	BuiltinStatefulResourcePolicyDelete BuiltinStatefulResourcePolicy = "Delete"
)

// Phase represents the current phase of the Sandbox0Infra
// +kubebuilder:validation:Enum=Installing;Ready;Degraded;Failed;Upgrading
type Phase string

const (
	PhaseInstalling Phase = "Installing"
	PhaseReady      Phase = "Ready"
	PhaseDegraded   Phase = "Degraded"
	PhaseFailed     Phase = "Failed"
	PhaseUpgrading  Phase = "Upgrading"
)

// Sandbox0InfraSpec defines the desired state of Sandbox0Infra
type Sandbox0InfraSpec struct {
	// Database configures the main database for sandbox0
	// +optional
	Database *DatabaseConfig `json:"database,omitempty"`

	// MetadataDatabase configures the S0FS metadata database
	// +optional
	MetadataDatabase *MetadataDatabaseConfig `json:"metadataDatabase,omitempty"`

	// Storage configures the storage backend (S0FS S3 backend)
	// +optional
	Storage *StorageConfig `json:"storage,omitempty"`

	// Redis configures a region-level Redis backend for shared services such as
	// distributed gateway rate limiting. When omitted, gateway rate limiting
	// uses process-local memory.
	// +optional
	Redis *RedisConfig `json:"redis,omitempty"`

	// CredentialVault configures a Vault-compatible backend for credential
	// sources that use hashicorp_vault storage or external references.
	// +optional
	CredentialVault *CredentialVaultConfig `json:"credentialVault,omitempty"`

	// Registry configures the container registry
	// +optional
	Registry *RegistryConfig `json:"registry,omitempty"`

	// ControlPlane configures external control plane connection.
	// +optional
	ControlPlane *ControlPlaneConfig `json:"controlPlane,omitempty"`

	// InternalAuth configures internal authentication keys
	// +optional
	InternalAuth *InternalAuthConfig `json:"internalAuth,omitempty"`

	// EnterpriseLicense configures the signed enterprise license used by
	// licensed services such as scheduler and regional-gateway.
	// +optional
	EnterpriseLicense *EnterpriseLicenseConfig `json:"enterpriseLicense,omitempty"`

	// Observability configures standard telemetry collection and export for
	// platform services.
	// +optional
	Observability *ObservabilityConfig `json:"observability,omitempty"`

	// ClickHouse configures the region-level ClickHouse data component used by
	// ClickHouse-backed features such as sandbox observability and metering.
	// +optional
	// +kubebuilder:default={type:disabled}
	ClickHouse *ClickHouseConfig `json:"clickHouse,omitempty"`

	// SandboxObservability configures the region-local historical per-sandbox
	// observability backend and collection pipelines.
	// +optional
	// +kubebuilder:default={type:disabled}
	SandboxObservability *SandboxObservabilityConfig `json:"sandboxObservability,omitempty"`

	// Metering configures the region usage ledger backend.
	// +optional
	// +kubebuilder:default={}
	Metering *MeteringConfig `json:"metering,omitempty"`

	// Services configures individual services
	// +optional
	// +kubebuilder:default={}
	Services *ServicesConfig `json:"services,omitempty"`

	// SandboxNodePlacement configures the shared node placement used by
	// sandbox workloads and node-local sandbox services.
	// +optional
	SandboxNodePlacement *SandboxNodePlacementConfig `json:"sandboxNodePlacement,omitempty"`

	// Region identifies the region for multi-cluster deployments
	// +optional
	Region string `json:"region,omitempty"`

	// PublicExposure configures public URL exposure for sandboxes
	// +optional
	PublicExposure *PublicExposureConfig `json:"publicExposure,omitempty"`

	// Cluster configures cluster identification and capacity
	// +optional
	Cluster *ClusterConfig `json:"cluster,omitempty"`

	// InitUser configures the initial admin user
	// +optional
	InitUser *InitUserConfig `json:"initUser,omitempty"`

	// BuiltinTemplates defines system builtin templates to seed the template store
	// +optional
	BuiltinTemplates []BuiltinTemplateConfig `json:"builtinTemplates,omitempty"`
}

// SandboxNodePlacementConfig defines shared scheduling constraints for sandbox
// workloads and node-local sandbox services.
type SandboxNodePlacementConfig struct {
	// NodeSelector constrains sandbox workloads and node-local sandbox services
	// onto a specific node set.
	// +optional
	NodeSelector map[string]string `json:"nodeSelector,omitempty"`

	// Tolerations allow sandbox workloads and node-local sandbox services to run
	// on tainted sandbox nodes.
	// +optional
	Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
}

// ObservabilityConfig configures standard telemetry collection for platform services.
type ObservabilityConfig struct {
	// ResourceAttributes are appended to OTEL_RESOURCE_ATTRIBUTES for every
	// platform component. Built-in service, region, cluster, and Kubernetes
	// attributes take precedence on key conflicts.
	// +optional
	ResourceAttributes map[string]string `json:"resourceAttributes,omitempty"`

	// Backend configures optional observability export integration. It is disabled
	// by default. Sandbox0 does not manage an observability storage backend.
	// +optional
	// +kubebuilder:default={type:disabled}
	Backend *ObservabilityBackendConfig `json:"backend,omitempty"`

	// Collection configures which signals sandbox0 should collect when
	// observability export is enabled. It does not define user-facing query APIs.
	// +optional
	Collection *ObservabilityCollectionConfig `json:"collection,omitempty"`

	// Traces configures trace export through standard OpenTelemetry env vars.
	// This legacy direct exporter config remains supported and takes precedence
	// over export-derived trace endpoint defaults.
	// +optional
	Traces *ObservabilityTracesConfig `json:"traces,omitempty"`
}

// ObservabilityBackendConfig configures optional telemetry export integration.
type ObservabilityBackendConfig struct {
	// Type selects the export integration mode. Defaults to disabled.
	// +kubebuilder:default=disabled
	// +optional
	Type ObservabilityBackendType `json:"type,omitempty"`

	// External configures integration with user-owned observability infrastructure.
	// +optional
	External *ExternalObservabilityBackendConfig `json:"external,omitempty"`
}

// ManagedObservabilityCollectorConfig configures operator-managed collectors.
type ManagedObservabilityCollectorConfig struct {
	// Image specifies the OpenTelemetry Collector contrib image.
	// +kubebuilder:default="otel/opentelemetry-collector-contrib:0.130.1"
	// +optional
	Image string `json:"image,omitempty"`

	// Replicas configures gateway collector replicas.
	// +kubebuilder:default=1
	// +optional
	Replicas *int32 `json:"replicas,omitempty"`

	// Resources overrides collector container resources.
	// +optional
	Resources *corev1.ResourceRequirements `json:"resources,omitempty"`
}

// ExternalObservabilityBackendConfig configures integration with user-owned
// observability infrastructure.
type ExternalObservabilityBackendConfig struct {
	// Mode selects whether sandbox0 deploys collectors or points services at an
	// existing collector endpoint. Defaults to existingCollector.
	// +kubebuilder:default=existingCollector
	// +optional
	Mode ObservabilityExternalMode `json:"mode,omitempty"`

	// OTLP configures the OTLP endpoint used for external export integration.
	// +optional
	OTLP *ObservabilityOTLPConfig `json:"otlp,omitempty"`

	// Collector configures the operator-managed collector when mode is
	// managedCollector.
	// +optional
	Collector *ManagedObservabilityCollectorConfig `json:"collector,omitempty"`
}

// ObservabilityOTLPConfig configures OTLP export to a collector or observability
// endpoint.
type ObservabilityOTLPConfig struct {
	// Endpoint is the OTLP gRPC endpoint.
	// +optional
	Endpoint string `json:"endpoint,omitempty"`

	// Headers are sent with OTLP exports.
	// +optional
	Headers map[string]string `json:"headers,omitempty"`

	// HeadersSecret references a secret containing OTLP headers formatted as an
	// OTEL_EXPORTER_OTLP_HEADERS key-value list.
	// +optional
	HeadersSecret *ObservabilityHeadersSecretRef `json:"headersSecret,omitempty"`

	// Insecure disables TLS verification when exporting through OTLP.
	// +optional
	Insecure *bool `json:"insecure,omitempty"`

	// Timeout bounds OTLP export requests.
	// +optional
	Timeout metav1.Duration `json:"timeout,omitempty"`
}

// ObservabilityHeadersSecretRef references OTLP headers stored in a Secret.
type ObservabilityHeadersSecretRef struct {
	// Name is the name of the Secret.
	Name string `json:"name"`

	// Key is the Secret key containing the header list.
	// +kubebuilder:default="headers"
	// +optional
	Key string `json:"key,omitempty"`
}

// ObservabilityCollectionConfig configures signal collection defaults for enabled
// observability export.
type ObservabilityCollectionConfig struct {
	// Logs configures platform and sandbox pod log collection.
	// +optional
	Logs *ObservabilitySignalCollectionConfig `json:"logs,omitempty"`

	// Metrics configures Prometheus-compatible metric scraping.
	// +optional
	Metrics *ObservabilitySignalCollectionConfig `json:"metrics,omitempty"`

	// Traces configures platform service trace collection.
	// +optional
	Traces *ObservabilitySignalCollectionConfig `json:"traces,omitempty"`
}

// ObservabilitySignalCollectionConfig configures a telemetry signal toggle.
type ObservabilitySignalCollectionConfig struct {
	// Enabled enables this signal. Defaults to true when observability export is
	// enabled.
	// +kubebuilder:default=true
	// +optional
	Enabled *bool `json:"enabled,omitempty"`
}

// ObservabilityTracesConfig configures OpenTelemetry trace export for platform
// services.
type ObservabilityTracesConfig struct {
	// Enabled enables trace exporter env injection. When false or omitted,
	// services keep their default noop tracer exporter.
	// +optional
	Enabled bool `json:"enabled,omitempty"`

	// Exporter is written to OTEL_TRACES_EXPORTER. Defaults to otlp when traces
	// are enabled.
	// +optional
	Exporter ObservabilityTraceExporter `json:"exporter,omitempty"`

	// Endpoint is written to OTEL_EXPORTER_OTLP_TRACES_ENDPOINT.
	// +optional
	Endpoint string `json:"endpoint,omitempty"`

	// Headers are written to OTEL_EXPORTER_OTLP_TRACES_HEADERS.
	// +optional
	Headers map[string]string `json:"headers,omitempty"`

	// Insecure is written to OTEL_EXPORTER_OTLP_TRACES_INSECURE when set.
	// +optional
	Insecure *bool `json:"insecure,omitempty"`

	// Timeout is written to OTEL_EXPORTER_OTLP_TRACES_TIMEOUT.
	// +optional
	Timeout metav1.Duration `json:"timeout,omitempty"`

	// SampleRate configures OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG.
	// Valid values are decimal strings between 0 and 1, for example "0.25".
	// +optional
	SampleRate string `json:"sampleRate,omitempty"`
}

// DatabaseConfig defines database configuration
type DatabaseConfig struct {
	// Type specifies the postgres database type: builtin, or external
	// +kubebuilder:default=builtin
	Type DatabaseType `json:"type,omitempty"`

	// Builtin configures the built-in single-node PostgreSQL
	// +optional
	// +kubebuilder:default={}
	Builtin *BuiltinDatabaseConfig `json:"builtin,omitempty"`

	// External configures connection to external database
	// +optional
	External *ExternalDatabaseConfig `json:"external,omitempty"`
}

// BuiltinDatabaseConfig defines built-in database configuration
// +kubebuilder:validation:XValidation:rule="has(self.persistence) == has(oldSelf.persistence)",message="persistence presence is immutable after creation"
type BuiltinDatabaseConfig struct {
	// Enabled enables the built-in database
	// +kubebuilder:default=true
	Enabled bool `json:"enabled,omitempty"`

	// Image specifies the postgres image for the builtin database
	// +kubebuilder:default="postgres:16-alpine"
	// +optional
	Image string `json:"image,omitempty"`

	// Port specifies the database port
	// +kubebuilder:default=5432
	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="port is immutable after creation"
	// +optional
	Port int32 `json:"port,omitempty"`

	// Username specifies the database username
	// +kubebuilder:default="sandbox0"
	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="username is immutable after creation"
	// +optional
	Username string `json:"username,omitempty"`

	// Database specifies the database name
	// +kubebuilder:default="sandbox0"
	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="database is immutable after creation"
	// +optional
	Database string `json:"database,omitempty"`

	// SSLMode specifies the SSL mode for builtin DSN
	// +kubebuilder:default="disable"
	// +optional
	SSLMode string `json:"sslMode,omitempty"`

	// Persistence configures database storage
	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="persistence is immutable after creation"
	// +optional
	Persistence *PersistenceConfig `json:"persistence,omitempty"`

	// StatefulResourcePolicy controls what happens to the builtin PVC and
	// generated credentials secret when the builtin database is disabled or
	// replaced by an external database.
	// +kubebuilder:default=Retain
	// +optional
	StatefulResourcePolicy BuiltinStatefulResourcePolicy `json:"statefulResourcePolicy,omitempty"`
}

// PersistenceConfig defines persistence configuration
type PersistenceConfig struct {
	// Size specifies the storage size
	// +kubebuilder:default="20Gi"
	Size resource.Quantity `json:"size,omitempty"`

	// StorageClass specifies the storage class (empty for default)
	// +optional
	StorageClass string `json:"storageClass,omitempty"`
}

// ExternalDatabaseConfig defines external database configuration
type ExternalDatabaseConfig struct {
	// Host specifies the database host
	Host string `json:"host"`

	// Port specifies the database port
	// +kubebuilder:default=5432
	Port int32 `json:"port,omitempty"`

	// Database specifies the database name
	Database string `json:"database"`

	// Username specifies the database username
	Username string `json:"username"`

	// PasswordSecret references the secret containing the password
	PasswordSecret SecretKeyRef `json:"passwordSecret"`

	// SSLMode specifies the SSL mode for connection
	// +kubebuilder:default="require"
	// +optional
	SSLMode string `json:"sslMode,omitempty"`
}

// SecretKeyRef references a key in a secret
type SecretKeyRef struct {
	// Name is the name of the secret
	// +optional
	Name string `json:"name"`

	// Key is the key in the secret
	// +kubebuilder:default="password"
	Key string `json:"key,omitempty"`
}

// MetadataDatabaseConfig defines S0FS metadata database configuration
type MetadataDatabaseConfig struct {
	// ShareWithMain uses the main database for S0FS metadata
	// +kubebuilder:default=true
	ShareWithMain bool `json:"shareWithMain,omitempty"`

	// External configures an independent database for S0FS
	// +optional
	External *ExternalDatabaseConfig `json:"external,omitempty"`
}

// RedisConfig defines region-level Redis configuration.
type RedisConfig struct {
	// Type specifies the Redis backend type: builtin or external.
	// +kubebuilder:default=builtin
	Type RedisType `json:"type,omitempty"`

	// Builtin configures the built-in single-node Redis instance.
	// +optional
	// +kubebuilder:default={}
	Builtin *BuiltinRedisConfig `json:"builtin,omitempty"`

	// External configures connection to an external Redis instance.
	// +optional
	External *ExternalRedisConfig `json:"external,omitempty"`

	// KeyPrefix prefixes keys used by sandbox0 Redis clients.
	// +optional
	// +kubebuilder:default="sandbox0"
	KeyPrefix string `json:"keyPrefix,omitempty"`

	// OperationTimeout bounds each Redis operation.
	// +optional
	// +kubebuilder:default="100ms"
	OperationTimeout metav1.Duration `json:"operationTimeout,omitempty"`

	// FailOpen allows traffic when Redis is temporarily unavailable.
	// +optional
	// +kubebuilder:default=true
	FailOpen *bool `json:"failOpen,omitempty"`
}

// BuiltinRedisConfig defines built-in Redis configuration.
type BuiltinRedisConfig struct {
	// Enabled enables the built-in Redis instance.
	// +kubebuilder:default=true
	Enabled bool `json:"enabled,omitempty"`

	// Image specifies the Redis image for the builtin instance.
	// +kubebuilder:default="redis:7-alpine"
	// +optional
	Image string `json:"image,omitempty"`

	// Port specifies the Redis port.
	// +kubebuilder:default=6379
	// +optional
	Port int32 `json:"port,omitempty"`
}

// ExternalRedisConfig defines external Redis configuration.
type ExternalRedisConfig struct {
	// URLSecret references a secret containing the Redis URL.
	URLSecret RedisURLSecretRef `json:"urlSecret"`
}

// RedisURLSecretRef references a Redis URL in a secret.
type RedisURLSecretRef struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// Key is the key containing the Redis URL.
	// +kubebuilder:default="url"
	// +optional
	Key string `json:"key,omitempty"`
}

// CredentialVaultConfig defines the region-level Vault-compatible credential backend.
type CredentialVaultConfig struct {
	// Type specifies the Vault backend type: builtin or external.
	// +kubebuilder:default=builtin
	Type CredentialVaultType `json:"type,omitempty"`

	// Builtin configures the built-in OpenBao instance.
	// +optional
	// +kubebuilder:default={}
	Builtin *BuiltinCredentialVaultConfig `json:"builtin,omitempty"`

	// External configures an existing HashiCorp Vault-compatible endpoint.
	// +optional
	External *ExternalCredentialVaultConfig `json:"external,omitempty"`
}

// BuiltinCredentialVaultConfig defines the built-in OpenBao configuration.
// +kubebuilder:validation:XValidation:rule="has(self.persistence) == has(oldSelf.persistence)",message="persistence presence is immutable after creation"
type BuiltinCredentialVaultConfig struct {
	// Enabled enables the built-in OpenBao instance.
	// +kubebuilder:default=true
	Enabled bool `json:"enabled,omitempty"`

	// Image specifies the OpenBao image for the builtin instance.
	// +kubebuilder:default="openbao/openbao:2.3.1"
	// +optional
	Image string `json:"image,omitempty"`

	// Port specifies the OpenBao API port.
	// +kubebuilder:default=8200
	// +optional
	Port int32 `json:"port,omitempty"`

	// Mount configures the KV v2 mount path used by Sandbox0.
	// +kubebuilder:default="secret"
	// +optional
	Mount string `json:"mount,omitempty"`

	// Persistence configures OpenBao storage.
	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="persistence is immutable after creation"
	// +optional
	Persistence *PersistenceConfig `json:"persistence,omitempty"`

	// StatefulResourcePolicy controls what happens to the builtin PVC and token
	// secret when the builtin vault is disabled or replaced by an external vault.
	// +kubebuilder:default=Retain
	// +optional
	StatefulResourcePolicy BuiltinStatefulResourcePolicy `json:"statefulResourcePolicy,omitempty"`
}

// ExternalCredentialVaultConfig defines an external HashiCorp Vault-compatible endpoint.
type ExternalCredentialVaultConfig struct {
	// Address is the base URL of the external Vault endpoint.
	Address string `json:"address"`

	// TokenSecret references a secret containing a Vault token for manager.
	TokenSecret CredentialVaultTokenSecretRef `json:"tokenSecret"`

	// CACertSecret optionally references a PEM CA bundle for the Vault endpoint.
	// +optional
	CACertSecret *CredentialVaultCACertSecretRef `json:"caCertSecret,omitempty"`

	// Namespace sets X-Vault-Namespace for Vault Enterprise deployments.
	// +optional
	Namespace string `json:"namespace,omitempty"`

	// Mount configures the default KV v2 mount path.
	// +kubebuilder:default="secret"
	// +optional
	Mount string `json:"mount,omitempty"`

	// SkipTLSVerify disables TLS verification for development-only external Vault endpoints.
	// +optional
	// +kubebuilder:default=false
	SkipTLSVerify bool `json:"skipTLSVerify,omitempty"`

	// AllowedPathPrefixes limits credential source references resolved through this connection.
	// The literal string {{teamID}} is replaced with the caller team id.
	// +optional
	AllowedPathPrefixes []string `json:"allowedPathPrefixes,omitempty"`
}

// CredentialVaultTokenSecretRef references a Vault token in a secret.
type CredentialVaultTokenSecretRef struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// Key is the key containing the Vault token.
	// +kubebuilder:default="token"
	// +optional
	Key string `json:"key,omitempty"`
}

// CredentialVaultCACertSecretRef references a Vault CA bundle in a secret.
type CredentialVaultCACertSecretRef struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// Key is the key containing the PEM CA bundle.
	// +kubebuilder:default="ca.crt"
	// +optional
	Key string `json:"key,omitempty"`
}

// StorageConfig defines storage backend configuration.
type StorageConfig struct {
	// Type specifies the storage type: builtin, s3, oss, or gcs.
	// +kubebuilder:default=builtin
	Type StorageType `json:"type,omitempty"`

	// Builtin configures the built-in RustFS storage
	// +optional
	// +kubebuilder:default={}
	Builtin *BuiltinStorageConfig `json:"builtin,omitempty"`

	// S3 configures S3 or S3-compatible storage
	// +optional
	S3 *S3StorageConfig `json:"s3,omitempty"`

	// OSS configures Aliyun OSS storage
	// +optional
	OSS *OSSStorageConfig `json:"oss,omitempty"`

	// GCS configures Google Cloud Storage using native GCS credentials.
	// +optional
	GCS *GCSStorageConfig `json:"gcs,omitempty"`
}

// BuiltinStorageConfig defines built-in storage configuration
// +kubebuilder:validation:XValidation:rule="has(self.persistence) == has(oldSelf.persistence)",message="persistence presence is immutable after creation"
// +kubebuilder:validation:XValidation:rule="has(self.credentials) == has(oldSelf.credentials)",message="credentials presence is immutable after creation"
type BuiltinStorageConfig struct {
	// Enabled enables the built-in storage
	// +kubebuilder:default=true
	Enabled bool `json:"enabled,omitempty"`

	// Image specifies the RustFS image for builtin storage
	// +kubebuilder:default="rustfs/rustfs:1.0.0-alpha.79"
	// +optional
	Image string `json:"image,omitempty"`

	// Port specifies the RustFS API port
	// +kubebuilder:default=9000
	// +optional
	Port int32 `json:"port,omitempty"`

	// ConsolePort specifies the RustFS console port
	// +kubebuilder:default=9001
	// +optional
	ConsolePort int32 `json:"consolePort,omitempty"`

	// Bucket specifies the default bucket name for builtin storage
	// +kubebuilder:default="sandbox0"
	// +optional
	Bucket string `json:"bucket,omitempty"`

	// Region specifies the default region for builtin storage
	// +kubebuilder:default="us-east-1"
	// +optional
	Region string `json:"region,omitempty"`

	// ConsoleEnabled enables the RustFS console
	// +kubebuilder:default=true
	// +optional
	ConsoleEnabled bool `json:"consoleEnabled,omitempty"`

	// Volumes specifies the RustFS data path
	// +kubebuilder:default="/data"
	// +optional
	Volumes string `json:"volumes,omitempty"`

	// ObsLogDirectory specifies the RustFS log directory
	// +kubebuilder:default="/data/logs"
	// +optional
	ObsLogDirectory string `json:"obsLogDirectory,omitempty"`

	// ObsLoggerLevel specifies the RustFS log level
	// +kubebuilder:default="debug"
	// +optional
	ObsLoggerLevel string `json:"obsLoggerLevel,omitempty"`

	// ObsEnvironment specifies the RustFS environment label
	// +kubebuilder:default="develop"
	// +optional
	ObsEnvironment string `json:"obsEnvironment,omitempty"`

	// Persistence configures storage persistence
	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="persistence is immutable after creation"
	// +optional
	Persistence *PersistenceConfig `json:"persistence,omitempty"`

	// Credentials configures access credentials (auto-generated if not specified)
	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="credentials are immutable after creation"
	// +optional
	Credentials *StorageCredentials `json:"credentials,omitempty"`

	// StatefulResourcePolicy controls what happens to the builtin PVC and
	// generated credentials secret when the builtin storage is disabled or
	// replaced by an external storage backend.
	// +kubebuilder:default=Retain
	// +optional
	StatefulResourcePolicy BuiltinStatefulResourcePolicy `json:"statefulResourcePolicy,omitempty"`
}

// StorageCredentials defines storage access credentials
type StorageCredentials struct {
	// AccessKey is the access key
	// +optional
	AccessKey string `json:"accessKey,omitempty"`

	// SecretKey is the secret key
	// +optional
	SecretKey string `json:"secretKey,omitempty"`
}

// S3StorageConfig defines S3 storage configuration
type S3StorageConfig struct {
	// Bucket specifies the S3 bucket name
	Bucket string `json:"bucket"`

	// Region specifies the AWS region
	Region string `json:"region"`

	// Endpoint specifies the S3 endpoint (optional for AWS)
	// +optional
	Endpoint string `json:"endpoint,omitempty"`

	// CredentialsSecret references the secret containing AWS credentials
	CredentialsSecret S3CredentialsSecret `json:"credentialsSecret"`

	// SessionTokenKey is the key for session token in the secret (optional)
	// +optional
	SessionTokenKey string `json:"sessionTokenKey,omitempty"`
}

// S3CredentialsSecret references S3 credentials in a secret
type S3CredentialsSecret struct {
	// Name is the name of the secret
	Name string `json:"name"`

	// AccessKeyKey is the key for access key ID
	// +kubebuilder:default="accessKeyId"
	AccessKeyKey string `json:"accessKeyKey,omitempty"`

	// SecretKeyKey is the key for secret access key
	// +kubebuilder:default="secretAccessKey"
	SecretKeyKey string `json:"secretKeyKey,omitempty"`
}

// OSSStorageConfig defines Aliyun OSS storage configuration
type OSSStorageConfig struct {
	// Bucket specifies the OSS bucket name
	Bucket string `json:"bucket"`

	// Region specifies the Aliyun region
	Region string `json:"region"`

	// Endpoint specifies the OSS endpoint
	Endpoint string `json:"endpoint"`

	// CredentialsSecret references the secret containing Aliyun credentials
	CredentialsSecret OSSCredentialsSecret `json:"credentialsSecret"`
}

// OSSCredentialsSecret references OSS credentials in a secret
type OSSCredentialsSecret struct {
	// Name is the name of the secret
	Name string `json:"name"`

	// AccessKeyKey is the key for access key ID
	// +kubebuilder:default="accessKeyId"
	AccessKeyKey string `json:"accessKeyKey,omitempty"`

	// SecretKeyKey is the key for access key secret
	// +kubebuilder:default="accessKeySecret"
	SecretKeyKey string `json:"secretKeyKey,omitempty"`
}

// GCSStorageConfig defines Google Cloud Storage configuration.
type GCSStorageConfig struct {
	// Bucket specifies the GCS bucket name.
	Bucket string `json:"bucket"`

	// WorkloadIdentityServiceAccountEmail is the Google service account email
	// used by storage clients through GKE Workload Identity.
	// +optional
	WorkloadIdentityServiceAccountEmail string `json:"workloadIdentityServiceAccountEmail,omitempty"`
}

// RegistryConfig defines container registry configuration.
type RegistryConfig struct {
	// Provider specifies the registry provider: builtin, aws, gcp, azure, aliyun, or harbor.
	// +kubebuilder:default=builtin
	// +kubebuilder:validation:Enum=builtin;aws;gcp;azure;aliyun;harbor
	Provider RegistryProvider `json:"provider,omitempty"`

	// ImagePullSecretName is the secret name to create in template namespaces.
	// +kubebuilder:default="sandbox0-registry-pull"
	// +optional
	ImagePullSecretName string `json:"imagePullSecretName,omitempty"`

	// Builtin configures the built-in registry.
	// +optional
	Builtin *BuiltinRegistryConfig `json:"builtin,omitempty"`

	// AWS configures AWS registry integration.
	// +optional
	AWS *AWSRegistryConfig `json:"aws,omitempty"`

	// GCP configures GCP registry integration.
	// +optional
	GCP *GCPRegistryConfig `json:"gcp,omitempty"`

	// Azure configures Azure registry integration.
	// +optional
	Azure *AzureRegistryConfig `json:"azure,omitempty"`

	// Aliyun configures Aliyun registry integration.
	// +optional
	Aliyun *AliyunRegistryConfig `json:"aliyun,omitempty"`

	// Harbor configures Harbor registry integration.
	// +optional
	Harbor *HarborRegistryConfig `json:"harbor,omitempty"`
}

// BuiltinRegistryConfig defines built-in registry configuration.
// +kubebuilder:validation:XValidation:rule="has(self.persistence) == has(oldSelf.persistence)",message="persistence presence is immutable after creation"
type BuiltinRegistryConfig struct {
	// Enabled enables the built-in registry.
	// +kubebuilder:default=true
	Enabled bool `json:"enabled,omitempty"`

	// Image specifies the registry image.
	// +kubebuilder:default="registry:2.8.3"
	// +optional
	Image string `json:"image,omitempty"`

	// Port specifies the registry port.
	// +kubebuilder:default=5000
	// +optional
	Port int32 `json:"port,omitempty"`

	// Persistence configures registry persistence.
	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="persistence is immutable after creation"
	// +optional
	Persistence *PersistenceConfig `json:"persistence,omitempty"`

	// Service configures the registry service exposure.
	// +optional
	Service *ServiceNetworkConfig `json:"service,omitempty"`

	// PushEndpoint overrides the external registry endpoint used for image push credentials.
	// Use host[:port] format, without scheme.
	// +optional
	PushEndpoint string `json:"pushEndpoint,omitempty"`

	// Ingress configures ingress settings for external registry access.
	// +optional
	Ingress *IngressConfig `json:"ingress,omitempty"`

	// CredentialsSecret references the secret containing registry credentials.
	// If omitted, the operator will generate a secret named "<infra-name>-registry-credentials".
	// +optional
	CredentialsSecret *RegistryCredentialsSecret `json:"credentialsSecret,omitempty"`

	// StatefulResourcePolicy controls what happens to the builtin registry PVC
	// when the builtin registry is disabled or replaced by an external
	// registry provider.
	// +kubebuilder:default=Retain
	// +optional
	StatefulResourcePolicy BuiltinStatefulResourcePolicy `json:"statefulResourcePolicy,omitempty"`
}

// RegistryCredentialsSecret references registry credentials in a secret.
type RegistryCredentialsSecret struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// UsernameKey is the key for username.
	// +kubebuilder:default="username"
	UsernameKey string `json:"usernameKey,omitempty"`

	// PasswordKey is the key for password.
	// +kubebuilder:default="password"
	PasswordKey string `json:"passwordKey,omitempty"`
}

// DockerConfigSecretRef references a dockerconfigjson secret.
type DockerConfigSecretRef struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// Key is the key in the secret.
	// +kubebuilder:default=".dockerconfigjson"
	Key string `json:"key,omitempty"`
}

// AWSRegistryConfig defines AWS registry configuration.
type AWSRegistryConfig struct {
	// Registry specifies the registry hostname.
	// +optional
	Registry string `json:"registry,omitempty"`

	// AssumeRoleARN optionally scopes registry pushes through an assumed IAM role.
	// +optional
	AssumeRoleARN string `json:"assumeRoleArn,omitempty"`

	// ExternalID is passed to STS AssumeRole when assumeRoleArn is configured.
	// +optional
	ExternalID string `json:"externalId,omitempty"`

	// Region specifies the AWS region.
	Region string `json:"region"`

	// RegistryID specifies the AWS account ID (optional).
	// +optional
	RegistryID string `json:"registryId,omitempty"`

	// PullSecret references the dockerconfigjson secret to use for image pulls.
	PullSecret DockerConfigSecretRef `json:"pullSecret"`

	// CredentialsSecret references AWS credentials for short-lived tokens.
	CredentialsSecret AWSRegistryCredentialsSecret `json:"credentialsSecret"`
}

// AWSRegistryCredentialsSecret references AWS credentials in a secret.
type AWSRegistryCredentialsSecret struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// AccessKeyKey is the key for access key ID.
	// +kubebuilder:default="accessKeyId"
	AccessKeyKey string `json:"accessKeyKey,omitempty"`

	// SecretKeyKey is the key for secret access key.
	// +kubebuilder:default="secretAccessKey"
	SecretKeyKey string `json:"secretKeyKey,omitempty"`

	// SessionTokenKey is the key for session token (optional).
	// +optional
	SessionTokenKey string `json:"sessionTokenKey,omitempty"`
}

// GCPRegistryConfig defines GCP registry configuration.
type GCPRegistryConfig struct {
	// Registry specifies the registry hostname.
	Registry string `json:"registry"`

	// PullSecret references the dockerconfigjson secret to use for image pulls.
	// Omit this on GKE when nodes or workload identity can already pull from
	// Artifact Registry without a namespace-local imagePullSecret.
	// +optional
	PullSecret *DockerConfigSecretRef `json:"pullSecret,omitempty"`

	// ServiceAccountSecret references the service account JSON key.
	// Omit this to use application default credentials inside manager.
	// +optional
	ServiceAccountSecret *GCPRegistryServiceAccountSecret `json:"serviceAccountSecret,omitempty"`
}

// GCPRegistryServiceAccountSecret references a service account key in a secret.
type GCPRegistryServiceAccountSecret struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// Key is the key in the secret.
	// +kubebuilder:default="serviceAccount.json"
	Key string `json:"key,omitempty"`
}

// AzureRegistryConfig defines Azure registry configuration.
type AzureRegistryConfig struct {
	// Registry specifies the registry hostname.
	Registry string `json:"registry"`

	// PullSecret references the dockerconfigjson secret to use for image pulls.
	PullSecret DockerConfigSecretRef `json:"pullSecret"`

	// CredentialsSecret references the client credentials for ACR.
	CredentialsSecret AzureRegistryCredentialsSecret `json:"credentialsSecret"`
}

// AzureRegistryCredentialsSecret references Azure credentials in a secret.
type AzureRegistryCredentialsSecret struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// TenantIDKey is the key for tenant ID.
	// +kubebuilder:default="tenantId"
	TenantIDKey string `json:"tenantIdKey,omitempty"`

	// ClientIDKey is the key for client ID.
	// +kubebuilder:default="clientId"
	ClientIDKey string `json:"clientIdKey,omitempty"`

	// ClientSecretKey is the key for client secret.
	// +kubebuilder:default="clientSecret"
	ClientSecretKey string `json:"clientSecretKey,omitempty"`
}

// AliyunRegistryConfig defines Aliyun registry configuration.
type AliyunRegistryConfig struct {
	// Registry specifies the registry hostname.
	Registry string `json:"registry"`

	// Region specifies the Aliyun region.
	Region string `json:"region"`

	// InstanceID specifies the ACR instance ID.
	InstanceID string `json:"instanceId"`

	// PullSecret references the dockerconfigjson secret to use for image pulls.
	PullSecret DockerConfigSecretRef `json:"pullSecret"`

	// CredentialsSecret references Aliyun credentials for short-lived tokens.
	CredentialsSecret AliyunRegistryCredentialsSecret `json:"credentialsSecret"`
}

// AliyunRegistryCredentialsSecret references Aliyun credentials in a secret.
type AliyunRegistryCredentialsSecret struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// AccessKeyKey is the key for access key ID.
	// +kubebuilder:default="accessKeyId"
	AccessKeyKey string `json:"accessKeyKey,omitempty"`

	// SecretKeyKey is the key for secret access key.
	// +kubebuilder:default="accessKeySecret"
	SecretKeyKey string `json:"secretKeyKey,omitempty"`
}

// HarborRegistryConfig defines Harbor registry configuration.
type HarborRegistryConfig struct {
	// Registry specifies the registry hostname.
	Registry string `json:"registry"`

	// PullSecret references the dockerconfigjson secret to use for image pulls.
	PullSecret DockerConfigSecretRef `json:"pullSecret"`

	// CredentialsSecret references Harbor credentials for push authentication.
	CredentialsSecret HarborRegistryCredentialsSecret `json:"credentialsSecret"`
}

// HarborRegistryCredentialsSecret references Harbor credentials in a secret.
type HarborRegistryCredentialsSecret struct {
	// Name is the name of the secret.
	Name string `json:"name"`

	// UsernameKey is the key for username.
	// +kubebuilder:default="username"
	UsernameKey string `json:"usernameKey,omitempty"`

	// PasswordKey is the key for password.
	// +kubebuilder:default="password"
	PasswordKey string `json:"passwordKey,omitempty"`
}

// ControlPlaneConfig defines external control plane configuration
type ControlPlaneConfig struct {
	// URL is the control plane regional-gateway URL
	URL string `json:"url"`

	// InternalAuthPublicKeySecret references the secret containing control plane's public key
	InternalAuthPublicKeySecret SecretKeyRef `json:"internalAuthPublicKeySecret"`
}

// InternalAuthConfig defines internal authentication configuration
type InternalAuthConfig struct {
	// ControlPlane configures control plane key pair
	// +optional
	ControlPlane *KeyPairConfig `json:"controlPlane,omitempty"`

	// DataPlane configures data plane key pair
	// +optional
	DataPlane *KeyPairConfig `json:"dataPlane,omitempty"`
}

// KeyPairConfig defines key pair configuration
type KeyPairConfig struct {
	// Generate enables automatic key generation
	// +kubebuilder:default=true
	Generate bool `json:"generate,omitempty"`

	// SecretRef references an existing secret containing the key pair
	// +optional
	SecretRef *KeyPairSecretRef `json:"secretRef,omitempty"`
}

// KeyPairSecretRef references a key pair in a secret
type KeyPairSecretRef struct {
	// Name is the name of the secret
	Name string `json:"name"`

	// PrivateKeyKey is the key for private key
	// +kubebuilder:default="private.key"
	PrivateKeyKey string `json:"privateKeyKey,omitempty"`

	// PublicKeyKey is the key for public key
	// +kubebuilder:default="public.key"
	PublicKeyKey string `json:"publicKeyKey,omitempty"`
}

// EnterpriseLicenseConfig defines the signed enterprise license source.
type EnterpriseLicenseConfig struct {
	// SecretRef references the secret containing the signed enterprise license.
	// When omitted, infra-operator falls back to <infra-name>-enterprise-license
	// with key license.lic for backward compatibility.
	// +optional
	SecretRef SecretKeyRef `json:"secretRef,omitempty"`
}

// ServicesConfig defines configuration for all services
type ServicesConfig struct {
	// GlobalGateway configures the global-gateway service (global service layer)
	// +optional
	// +kubebuilder:default={}
	GlobalGateway *GlobalGatewayServiceConfig `json:"globalGateway,omitempty"`

	// RegionalGateway configures the regional-gateway service (control plane)
	// +optional
	// +kubebuilder:default={}
	RegionalGateway *RegionalGatewayServiceConfig `json:"regionalGateway,omitempty"`

	// SSHGateway configures the ssh-gateway service (control plane)
	// +optional
	// +kubebuilder:default={}
	SSHGateway *SSHGatewayServiceConfig `json:"sshGateway,omitempty"`

	// Scheduler configures the scheduler service (control plane)
	// +optional
	Scheduler *SchedulerServiceConfig `json:"scheduler,omitempty"`

	// ClusterGateway configures the cluster-gateway service (data plane)
	// +optional
	// +kubebuilder:default={}
	ClusterGateway *ClusterGatewayServiceConfig `json:"clusterGateway,omitempty"`

	// Manager configures the manager service (data plane)
	// +optional
	// +kubebuilder:default={}
	Manager *ManagerServiceConfig `json:"manager,omitempty"`

	// Ctld configures the ctld node-local service (data plane)
	// +optional
	// +kubebuilder:default={}
	Ctld *CtldServiceConfig `json:"ctld,omitempty"`

	// StorageProxy configures the storage-proxy service (data plane)
	// +optional
	// +kubebuilder:default={}
	StorageProxy *StorageProxyServiceConfig `json:"storageProxy,omitempty"`

	// Netd configures the netd service (data plane)
	// +optional
	// +kubebuilder:default={}
	Netd *NetdServiceConfig `json:"netd,omitempty"`
}

// EnabledServiceConfig defines the enabled state shared by all services.
type EnabledServiceConfig struct {
	// Enabled enables or disables the service
	// +kubebuilder:default=false
	Enabled bool `json:"enabled,omitempty"`
}

// WorkloadServiceConfig defines shared deployment-style service settings.
type WorkloadServiceConfig struct {
	EnabledServiceConfig `json:",inline"`
	// Replicas specifies the number of replicas
	// +kubebuilder:default=1
	Replicas int32 `json:"replicas,omitempty"`

	// Resources specifies resource requirements
	// +optional
	Resources *corev1.ResourceRequirements `json:"resources,omitempty"`
}

// ServiceExposureConfig defines Kubernetes Service exposure settings.
type ServiceExposureConfig struct {
	// Service configures the Kubernetes service
	// +optional
	Service *ServiceNetworkConfig `json:"service,omitempty"`
}

// IngressExposureConfig defines ingress exposure settings.
type IngressExposureConfig struct {
	// Ingress configures ingress settings
	// +optional
	Ingress *IngressConfig `json:"ingress,omitempty"`
}

// GlobalGatewayServiceConfig defines configuration for global-gateway service.
type GlobalGatewayServiceConfig struct {
	WorkloadServiceConfig `json:",inline"`
	ServiceExposureConfig `json:",inline"`
	IngressExposureConfig `json:",inline"`
	// Config contains global-gateway specific configuration
	// +optional
	// +kubebuilder:default={}
	Config *GlobalGatewayConfig `json:"config,omitempty"`
}

// RegionalGatewayServiceConfig defines configuration for regional-gateway service
type RegionalGatewayServiceConfig struct {
	WorkloadServiceConfig `json:",inline"`
	ServiceExposureConfig `json:",inline"`
	IngressExposureConfig `json:",inline"`
	// Config contains regional-gateway specific configuration
	// +optional
	// +kubebuilder:default={}
	Config *RegionalGatewayConfig `json:"config,omitempty"`
}

// SSHGatewayServiceConfig defines configuration for ssh-gateway service.
type SSHGatewayServiceConfig struct {
	WorkloadServiceConfig `json:",inline"`
	ServiceExposureConfig `json:",inline"`
	// EndpointPort optionally overrides the SSH port advertised by regional and
	// cluster gateways. Use this when an external load balancer listens on a
	// different port than the Kubernetes Service or NodePort.
	// +optional
	// +kubebuilder:validation:Minimum=1
	// +kubebuilder:validation:Maximum=65535
	EndpointPort int32 `json:"endpointPort,omitempty"`
	// Config contains ssh-gateway specific configuration.
	// +optional
	// +kubebuilder:default={}
	Config *SSHGatewayConfig `json:"config,omitempty"`
}

// SchedulerServiceConfig defines configuration for scheduler service
type SchedulerServiceConfig struct {
	WorkloadServiceConfig `json:",inline"`
	ServiceExposureConfig `json:",inline"`
	// Config contains scheduler specific configuration
	// +optional
	// +kubebuilder:default={}
	Config *SchedulerConfig `json:"config,omitempty"`
}

// ClusterGatewayServiceConfig defines configuration for cluster-gateway service
type ClusterGatewayServiceConfig struct {
	WorkloadServiceConfig `json:",inline"`
	ServiceExposureConfig `json:",inline"`
	// Config contains cluster-gateway specific configuration
	// +optional
	// +kubebuilder:default={}
	Config *ClusterGatewayConfig `json:"config,omitempty"`
}

// ManagerServiceConfig defines configuration for manager service
type ManagerServiceConfig struct {
	WorkloadServiceConfig `json:",inline"`
	ServiceExposureConfig `json:",inline"`
	// Config contains manager specific configuration
	// +optional
	// +kubebuilder:default={}
	Config *ManagerConfig `json:"config,omitempty"`
}

// StorageProxyServiceConfig defines configuration for storage-proxy service
type StorageProxyServiceConfig struct {
	WorkloadServiceConfig `json:",inline"`
	ServiceExposureConfig `json:",inline"`
	// Config contains storage-proxy specific configuration
	// +optional
	// +kubebuilder:default={}
	Config *StorageProxyConfig `json:"config,omitempty"`
}

// CtldServiceConfig defines configuration for the ctld daemonset.
type CtldServiceConfig struct {
	// ContainerdHostDataRoot is the containerd data root path on the host.
	// Set this when nodes use a non-default containerd root such as a dedicated
	// sandbox worker data disk.
	// +optional
	// +kubebuilder:default=/var/lib/containerd
	// +kubebuilder:validation:Pattern=`^/.*`
	ContainerdHostDataRoot string `json:"containerdHostDataRoot,omitempty"`

	// RootFSObjectCacheMaxBytes caps the ctld node-local rootfs object cache.
	// Set 0 to disable the cache.
	// +kubebuilder:default="20Gi"
	// +optional
	RootFSObjectCacheMaxBytes string `json:"rootfsObjectCacheMaxBytes,omitempty"`

	// RootFSObjectCacheMinFreeBytes asks ctld GC to keep at least this much free
	// space on the cache filesystem.
	// +optional
	RootFSObjectCacheMinFreeBytes string `json:"rootfsObjectCacheMinFreeBytes,omitempty"`

	// RootFSObjectCacheMaxAge evicts cache entries older than this age. Zero
	// disables age-based eviction.
	// +optional
	RootFSObjectCacheMaxAge metav1.Duration `json:"rootfsObjectCacheMaxAge,omitempty"`

	// RootFSObjectCacheSweepInterval controls how often ctld sweeps the
	// node-local rootfs object cache.
	// +optional
	RootFSObjectCacheSweepInterval metav1.Duration `json:"rootfsObjectCacheSweepInterval,omitempty"`
}

// NetdServiceConfig defines configuration for netd service
type NetdServiceConfig struct {
	EnabledServiceConfig `json:",inline"`
	// MITMCASecretName overrides the operator-managed cluster-local MITM CA secret for HTTPS interception.
	// Expected keys are ca.crt and ca.key. When unset, infra-operator generates and reuses a managed secret.
	// +optional
	MITMCASecretName string `json:"mitmCaSecretName,omitempty"`
	// RuntimeClassName specifies the Kubernetes runtime class for the netd daemonset.
	// Use a host-compatible runtime such as runc. Do not run netd on gVisor or Kata.
	// +optional
	RuntimeClassName *string `json:"runtimeClassName,omitempty"`
	// NodeSelector constrains netd onto a specific node set.
	// Deprecated: use spec.sandboxNodePlacement.nodeSelector instead. This field
	// remains as a backward-compatible alias when the shared placement is unset.
	// +optional
	NodeSelector map[string]string `json:"nodeSelector,omitempty"`
	// Tolerations allow netd to run on tainted sandbox nodes.
	// Deprecated: use spec.sandboxNodePlacement.tolerations instead. This field
	// remains as a backward-compatible alias when the shared placement is unset.
	// +optional
	Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
	// Config contains netd specific configuration
	// +optional
	// +kubebuilder:default={}
	Config *NetdConfig `json:"config,omitempty"`
}

// IsGlobalGatewayEnabled returns true when global-gateway is enabled.
func IsGlobalGatewayEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Services == nil || infra.Spec.Services.GlobalGateway == nil {
		return false
	}
	return infra.Spec.Services.GlobalGateway.Enabled
}

// IsRegionalGatewayEnabled returns true when regional-gateway is enabled.
func IsRegionalGatewayEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Services == nil || infra.Spec.Services.RegionalGateway == nil {
		return false
	}
	return infra.Spec.Services.RegionalGateway.Enabled
}

// IsSSHGatewayEnabled returns true when ssh-gateway is enabled.
func IsSSHGatewayEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Services == nil || infra.Spec.Services.SSHGateway == nil {
		return false
	}
	return infra.Spec.Services.SSHGateway.Enabled
}

// IsSchedulerEnabled returns true when scheduler is enabled.
func IsSchedulerEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Services == nil || infra.Spec.Services.Scheduler == nil {
		return false
	}
	return infra.Spec.Services.Scheduler.Enabled
}

// IsClusterGatewayEnabled returns true when cluster-gateway is enabled.
func IsClusterGatewayEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Services == nil || infra.Spec.Services.ClusterGateway == nil {
		return false
	}
	return infra.Spec.Services.ClusterGateway.Enabled
}

// IsManagerEnabled returns true when manager is enabled.
func IsManagerEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Services == nil || infra.Spec.Services.Manager == nil {
		return false
	}
	return infra.Spec.Services.Manager.Enabled
}

// IsStorageProxyEnabled returns true when storage-proxy is enabled.
func IsStorageProxyEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Services == nil || infra.Spec.Services.StorageProxy == nil {
		return false
	}
	return infra.Spec.Services.StorageProxy.Enabled
}

// IsNetdEnabled returns true when netd is enabled.
func IsNetdEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Services == nil || infra.Spec.Services.Netd == nil {
		return false
	}
	return infra.Spec.Services.Netd.Enabled
}

// IsDatabaseEnabled returns true when database should be reconciled.
func IsDatabaseEnabled(infra *Sandbox0Infra) bool {
	if infra == nil {
		return false
	}
	if infra.Spec.Database == nil {
		return false
	}
	switch infra.Spec.Database.Type {
	case DatabaseTypeBuiltin:
		if infra.Spec.Database.Builtin != nil {
			return infra.Spec.Database.Builtin.Enabled
		}
		return true
	case DatabaseTypeExternal:
		return true
	default:
		return true
	}
}

// IsStorageEnabled returns true when storage should be reconciled.
func IsStorageEnabled(infra *Sandbox0Infra) bool {
	if infra == nil {
		return false
	}
	if infra.Spec.Storage == nil {
		return false
	}
	switch infra.Spec.Storage.Type {
	case StorageTypeBuiltin:
		if infra.Spec.Storage.Builtin != nil {
			return infra.Spec.Storage.Builtin.Enabled
		}
		return true
	case StorageTypeS3, StorageTypeOSS, StorageTypeGCS:
		return true
	default:
		return true
	}
}

// IsRedisEnabled returns true when Redis should be reconciled and injected into
// Redis-capable services.
func IsRedisEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Redis == nil {
		return false
	}
	var builtinEnabled *bool
	if infra.Spec.Redis.Builtin != nil {
		builtinEnabled = &infra.Spec.Redis.Builtin.Enabled
	}
	return rediscache.SpecEnabled(true, string(infra.Spec.Redis.Type), builtinEnabled)
}

// IsClickHouseEnabled returns true when the region-level ClickHouse component
// should be reconciled and injected into ClickHouse-backed features.
func IsClickHouseEnabled(infra *Sandbox0Infra) bool {
	if infra == nil {
		return false
	}
	if infra.Spec.ClickHouse == nil {
		return legacySandboxObservabilityUsesClickHouse(infra)
	}
	switch infra.Spec.ClickHouse.Type {
	case ClickHouseTypeBuiltin:
		if infra.Spec.ClickHouse.Builtin != nil {
			return infra.Spec.ClickHouse.Builtin.Enabled
		}
		return true
	case ClickHouseTypeExternal:
		return true
	default:
		return false
	}
}

// IsSandboxObservabilityEnabled returns true when the region-level per-sandbox
// historical observability backend should be reconciled and injected.
func IsSandboxObservabilityEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.SandboxObservability == nil {
		return false
	}
	if infra.Spec.SandboxObservability.Enabled != nil {
		if !*infra.Spec.SandboxObservability.Enabled {
			return false
		}
		backend := infra.Spec.SandboxObservability.Backend
		return backend == "" || backend == SandboxObservabilityBackendClickHouse
	}
	if infra.Spec.SandboxObservability.Backend == SandboxObservabilityBackendClickHouse {
		return true
	}
	switch infra.Spec.SandboxObservability.Type {
	case SandboxObservabilityTypeBuiltin:
		if infra.Spec.SandboxObservability.Builtin != nil {
			return infra.Spec.SandboxObservability.Builtin.Enabled
		}
		return true
	case SandboxObservabilityTypeExternal:
		return true
	default:
		return false
	}
}

// IsMeteringEnabled returns true when the region usage ledger should be
// reconciled and injected. Metering is disabled by default.
func IsMeteringEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Metering == nil {
		return false
	}
	return infra.Spec.Metering.Enabled != nil && *infra.Spec.Metering.Enabled
}

func legacySandboxObservabilityUsesClickHouse(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.SandboxObservability == nil {
		return false
	}
	switch infra.Spec.SandboxObservability.Type {
	case SandboxObservabilityTypeBuiltin:
		if infra.Spec.SandboxObservability.Builtin != nil {
			return infra.Spec.SandboxObservability.Builtin.Enabled
		}
		return true
	case SandboxObservabilityTypeExternal:
		return true
	default:
		return false
	}
}

// IsCredentialVaultEnabled returns true when a Vault-compatible credential backend should be reconciled.
func IsCredentialVaultEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.CredentialVault == nil {
		return false
	}
	switch infra.Spec.CredentialVault.Type {
	case CredentialVaultTypeBuiltin, "":
		if infra.Spec.CredentialVault.Builtin != nil {
			return infra.Spec.CredentialVault.Builtin.Enabled
		}
		return true
	case CredentialVaultTypeExternal:
		return true
	default:
		return true
	}
}

// IsRegistryEnabled returns true when registry should be reconciled.
func IsRegistryEnabled(infra *Sandbox0Infra) bool {
	if infra == nil || infra.Spec.Registry == nil {
		return false
	}
	switch infra.Spec.Registry.Provider {
	case RegistryProviderBuiltin:
		if infra.Spec.Registry.Builtin != nil {
			return infra.Spec.Registry.Builtin.Enabled
		}
		return true
	case RegistryProviderAWS, RegistryProviderGCP, RegistryProviderAzure, RegistryProviderAliyun, RegistryProviderHarbor:
		return true
	default:
		return true
	}
}

// HasControlPlaneServices returns true when any control-plane service is enabled.
func HasControlPlaneServices(infra *Sandbox0Infra) bool {
	return IsRegionalGatewayEnabled(infra) || IsSSHGatewayEnabled(infra) || IsSchedulerEnabled(infra)
}

// HasDataPlaneServices returns true when any data-plane service is enabled.
func HasDataPlaneServices(infra *Sandbox0Infra) bool {
	return IsClusterGatewayEnabled(infra) || IsManagerEnabled(infra) || IsStorageProxyEnabled(infra) || IsNetdEnabled(infra)
}

// HasAnyServiceEnabled returns true when at least one service is enabled.
func HasAnyServiceEnabled(infra *Sandbox0Infra) bool {
	return IsGlobalGatewayEnabled(infra) || HasControlPlaneServices(infra) || HasDataPlaneServices(infra)
}

// ServiceNetworkConfig defines service network configuration
// +kubebuilder:validation:XValidation:rule="self.type != 'NodePort' || self.port == 0 || (self.port >= 30000 && self.port <= 32767)",message="port must be within 30000-32767 when type is NodePort"
type ServiceNetworkConfig struct {
	// Type specifies the service type
	// +kubebuilder:default="ClusterIP"
	Type corev1.ServiceType `json:"type,omitempty"`

	// Port specifies the service port
	// +kubebuilder:default=80
	Port int32 `json:"port,omitempty"`

	// Annotations declares Service metadata annotations.
	// Use this for cloud-provider specific load balancer configuration such as
	// ACM certificate attachment on AWS.
	// +optional
	Annotations map[string]string `json:"annotations,omitempty"`
}

// IngressConfig defines ingress configuration
type IngressConfig struct {
	// Enabled enables ingress
	// +kubebuilder:default=false
	Enabled bool `json:"enabled,omitempty"`

	// Annotations specifies provider-specific ingress annotations.
	// +optional
	Annotations map[string]string `json:"annotations,omitempty"`

	// ClassName specifies the ingress class name
	// +optional
	ClassName string `json:"className,omitempty"`

	// Host specifies the ingress host
	// +optional
	Host string `json:"host,omitempty"`

	// ExtraHosts specifies additional ingress hosts routed to the same backend.
	// +optional
	ExtraHosts []string `json:"extraHosts,omitempty"`

	// TLSSecret specifies the TLS secret name
	// +optional
	TLSSecret string `json:"tlsSecret,omitempty"`

	// TLS specifies host groups and their backing TLS secrets.
	// When set, TLS takes precedence over TLSSecret.
	// +optional
	TLS []IngressTLSConfig `json:"tls,omitempty"`
}

// IngressTLSConfig defines one TLS certificate binding for ingress hosts.
type IngressTLSConfig struct {
	// Hosts specifies the hosts covered by this TLS secret.
	// +optional
	Hosts []string `json:"hosts,omitempty"`

	// SecretName specifies the Kubernetes TLS secret name.
	// +optional
	SecretName string `json:"secretName,omitempty"`
}

// PublicExposureConfig defines public URL exposure configuration for sandboxes.
// Host format: <exposureLabel>.<regionLabel>.<rootDomain>
type PublicExposureConfig struct {
	// Enabled enables public exposure routing
	// +optional
	// +kubebuilder:default=true
	Enabled bool `json:"enabled,omitempty"`

	// RootDomain is the root domain for public exposure URLs
	// +optional
	// +kubebuilder:default="sandbox0.app"
	RootDomain string `json:"rootDomain,omitempty"`

	// RegionID is the DNS-safe region label used in public URLs.
	// It is not the canonical multi-region tenancy identifier.
	// +optional
	// +kubebuilder:default="aws-us-east-1"
	RegionID string `json:"regionId,omitempty"`
}

// ClusterConfig defines cluster identification and capacity
type ClusterConfig struct {
	// ID is the unique Sandbox0 data-plane cluster identifier used in routing and sandbox names.
	// It is separate from the provider cluster name and must stay short enough for sandbox name encoding.
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:MaxLength=20
	// +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
	ID string `json:"id"`

	// Name is the human-readable cluster name
	// +optional
	Name string `json:"name,omitempty"`

	// Capacity specifies cluster resource capacity
	// +optional
	Capacity *ClusterCapacity `json:"capacity,omitempty"`
}

// ClusterCapacity defines cluster resource capacity
type ClusterCapacity struct {
	// MaxSandboxes is the maximum number of sandboxes
	// +optional
	MaxSandboxes int32 `json:"maxSandboxes,omitempty"`

	// CPU specifies CPU capacity
	// +optional
	CPU *ResourceCapacity `json:"cpu,omitempty"`

	// Memory specifies memory capacity
	// +optional
	Memory *ResourceCapacity `json:"memory,omitempty"`
}

// ResourceCapacity defines resource capacity
type ResourceCapacity struct {
	// Total is the total capacity
	Total string `json:"total,omitempty"`

	// Available is the available capacity
	Available string `json:"available,omitempty"`
}

// InitUserConfig defines initial admin user configuration
type InitUserConfig struct {
	// Email is the admin user's email
	// +optional
	Email string `json:"email"`

	// PasswordSecret references the secret containing the password
	// +optional
	PasswordSecret SecretKeyRef `json:"passwordSecret"`

	// Name is the admin user's display name
	// +optional
	Name string `json:"name,omitempty"`

	// HomeRegionID is required for global-gateway init users so the bootstrap team is routable.
	// +optional
	HomeRegionID string `json:"homeRegionId,omitempty"`
}

// BuiltinTemplateConfig defines a system builtin template.
type BuiltinTemplateConfig struct {
	TemplateID  string                    `json:"templateId"`
	Image       string                    `json:"image,omitempty"`
	DisplayName string                    `json:"displayName,omitempty"`
	Description string                    `json:"description,omitempty"`
	Pool        BuiltinTemplatePoolConfig `json:"pool,omitempty"`
	// Spec optionally overrides the full template spec seeded by infra-operator.
	// When omitted, infra-operator builds the spec from the legacy fields above
	// and the built-in preset for the template ID.
	// +optional
	Spec *templatev1alpha1.SandboxTemplateSpec `json:"spec,omitempty"`
}

// BuiltinTemplatePoolConfig holds pool defaults for builtin templates.
type BuiltinTemplatePoolConfig struct {
	// +optional
	// +kubebuilder:default=1
	MinIdle *int32 `json:"minIdle,omitempty"`
	// +optional
	// +kubebuilder:default=5
	MaxIdle *int32 `json:"maxIdle,omitempty"`
}

// Sandbox0InfraStatus defines the observed state of Sandbox0Infra
type Sandbox0InfraStatus struct {
	// Phase represents the current phase of the infrastructure
	// +optional
	Phase Phase `json:"phase,omitempty"`

	// Conditions represent the latest available observations of the infrastructure's state
	// +optional
	Conditions []metav1.Condition `json:"conditions,omitempty"`

	// ObservedVersion is the last observed version
	// +optional
	ObservedVersion string `json:"observedVersion,omitempty"`

	// Endpoints contains the service endpoints
	// +optional
	Endpoints *EndpointsStatus `json:"endpoints,omitempty"`

	// Cluster contains cluster registration status
	// +optional
	Cluster *ClusterStatus `json:"cluster,omitempty"`

	// InternalAuth contains internal authentication status
	// +optional
	InternalAuth *InternalAuthStatus `json:"internalAuth,omitempty"`

	// LastOperation contains the last operation information
	// +optional
	LastOperation *LastOperation `json:"lastOperation,omitempty"`

	// LastMessage is the most recent status message (success or error)
	// +optional
	LastMessage string `json:"lastMessage,omitempty"`

	// Progress shows readiness progress in "ready/total" format
	// +optional
	Progress string `json:"progress,omitempty"`

	// RetainedResources lists builtin stateful resources intentionally retained
	// after builtin-to-external or enabled-to-disabled transitions.
	// +optional
	RetainedResources []RetainedResourceStatus `json:"retainedResources,omitempty"`
}

// EndpointsStatus contains service endpoints
type EndpointsStatus struct {
	// GlobalGateway is the global-gateway URL
	// +optional
	GlobalGateway string `json:"globalGateway,omitempty"`

	// RegionalGateway is the external regional-gateway URL
	// +optional
	RegionalGateway string `json:"regionalGateway,omitempty"`

	// RegionalGatewayInternal is the internal regional-gateway URL
	// +optional
	RegionalGatewayInternal string `json:"regionalGatewayInternal,omitempty"`

	// ClusterGateway is the cluster-gateway URL
	// +optional
	ClusterGateway string `json:"clusterGateway,omitempty"`
}

// ClusterStatus contains cluster registration status
type ClusterStatus struct {
	// ID is the cluster identifier
	// +optional
	ID string `json:"id,omitempty"`

	// Registered indicates if the cluster is registered
	// +optional
	Registered bool `json:"registered,omitempty"`

	// RegisteredAt is the registration timestamp
	// +optional
	RegisteredAt *metav1.Time `json:"registeredAt,omitempty"`
}

// RetainedResourceStatus describes a builtin stateful resource retained by
// policy after a lifecycle transition.
type RetainedResourceStatus struct {
	// Component is the logical component name, for example database or storage.
	Component string `json:"component"`

	// Kind is the Kubernetes resource kind.
	Kind string `json:"kind"`

	// Name is the Kubernetes resource name.
	Name string `json:"name"`

	// Policy is the stateful resource policy that caused the resource to be retained.
	Policy BuiltinStatefulResourcePolicy `json:"policy,omitempty"`

	// Reason explains why the resource is retained.
	// +optional
	Reason string `json:"reason,omitempty"`
}

// InternalAuthStatus contains internal authentication status
type InternalAuthStatus struct {
	// ControlPlanePublicKey references the control plane public key
	// +optional
	ControlPlanePublicKey *SecretKeyStatus `json:"controlPlanePublicKey,omitempty"`

	// DataPlanePublicKey references the data plane public key
	// +optional
	DataPlanePublicKey *SecretKeyStatus `json:"dataPlanePublicKey,omitempty"`
}

// SecretKeyStatus references a key in a secret
type SecretKeyStatus struct {
	// SecretName is the name of the secret
	SecretName string `json:"secretName"`

	// SecretKey is the key in the secret
	SecretKey string `json:"secretKey"`
}

// LastOperation contains the last operation information
type LastOperation struct {
	// Type is the operation type
	// +optional
	Type string `json:"type,omitempty"`

	// StartedAt is when the operation started
	// +optional
	StartedAt *metav1.Time `json:"startedAt,omitempty"`

	// CompletedAt is when the operation completed
	// +optional
	CompletedAt *metav1.Time `json:"completedAt,omitempty"`

	// Status is the operation status
	// +optional
	Status string `json:"status,omitempty"`

	// Error contains error message if failed
	// +optional
	Error string `json:"error,omitempty"`
}

// Condition types
const (
	ConditionTypeReady                     = "Ready"
	ConditionTypeDatabaseReady             = "DatabaseReady"
	ConditionTypeRedisReady                = "RedisReady"
	ConditionTypeCredentialVaultReady      = "CredentialVaultReady"
	ConditionTypeStorageReady              = "StorageReady"
	ConditionTypeRegistryReady             = "RegistryReady"
	ConditionTypeObservabilityReady        = "ObservabilityReady"
	ConditionTypeClickHouseReady           = "ClickHouseReady"
	ConditionTypeMeteringReady             = "MeteringReady"
	ConditionTypeSandboxObservabilityReady = "SandboxObservabilityReady"
	ConditionTypeGlobalGatewayReady        = "GlobalGatewayReady"
	ConditionTypeRegionalGatewayReady      = "RegionalGatewayReady"
	ConditionTypeSSHGatewayReady           = "SSHGatewayReady"
	ConditionTypeClusterGatewayReady       = "ClusterGatewayReady"
	ConditionTypeManagerReady              = "ManagerReady"
	ConditionTypeStorageProxyReady         = "StorageProxyReady"
	ConditionTypeCtldReady                 = "CtldReady"
	ConditionTypeNetdReady                 = "NetdReady"
	ConditionTypeSchedulerReady            = "SchedulerReady"
	ConditionTypeInternalAuthReady         = "InternalAuthReady"
	ConditionTypeCRDsInstalled             = "CRDsInstalled"
	ConditionTypeSecretsGenerated          = "SecretsGenerated"
	ConditionTypeInitUserReady             = "InitUserReady"
	ConditionTypeClusterRegistered         = "ClusterRegistered"
)

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:resource:shortName=s0i
//+kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
//+kubebuilder:printcolumn:name="Progress",type=string,JSONPath=`.status.progress`
//+kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.lastMessage`
//+kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

// Sandbox0Infra is the Schema for the sandbox0infras API
type Sandbox0Infra struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   Sandbox0InfraSpec   `json:"spec,omitempty"`
	Status Sandbox0InfraStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// Sandbox0InfraList contains a list of Sandbox0Infra
type Sandbox0InfraList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []Sandbox0Infra `json:"items"`
}

func init() {
	SchemeBuilder.Register(&Sandbox0Infra{}, &Sandbox0InfraList{})
}
