// +kubebuilder:object:generate=true
package config

import (
	"fmt"
	"os"
	"strconv"
	"time"

	"gopkg.in/yaml.v3"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ManagerConfig holds the configuration for the manager.
type ManagerConfig struct {
	// HTTP Server
	// +optional
	// +kubebuilder:default=8080
	HTTPPort int `yaml:"http_port" json:"httpPort"`

	// manager docker image, used to copy the procd binary to sandbox pod
	ManagerImage string `yaml:"manager_image" json:"-"`

	DefaultClusterId string `yaml:"default_cluster_id" json:"-"`
	RegionID         string `yaml:"region_id" json:"-"`
	// +optional
	// +kubebuilder:default=true
	TemplateStoreEnabled bool `yaml:"template_store_enabled" json:"-"`

	// Kubernetes
	// +optional
	KubeConfig string `yaml:"kube_config" json:"kubeConfig"`
	// +optional
	// +kubebuilder:default=true
	LeaderElection bool `yaml:"leader_election" json:"leaderElection"`
	// +optional
	// +kubebuilder:default="30s"
	ResyncPeriod metav1.Duration `yaml:"resync_period" json:"resyncPeriod"`

	// Database
	DatabaseURL string `yaml:"database_url" json:"-"`
	// +optional
	// +kubebuilder:default=10
	DatabaseMaxConns int32 `yaml:"database_max_conns" json:"databaseMaxConns"`
	// +optional
	// +kubebuilder:default=2
	DatabaseMinConns int32 `yaml:"database_min_conns" json:"databaseMinConns"`

	// Cleanup Controller
	// +optional
	// +kubebuilder:default="60s"
	CleanupInterval metav1.Duration `yaml:"cleanup_interval" json:"cleanupInterval"`

	// Logging
	// +optional
	// +kubebuilder:default="info"
	LogLevel string `yaml:"log_level" json:"logLevel"`

	// Metrics
	// +optional
	// +kubebuilder:default=9090
	MetricsPort int `yaml:"metrics_port" json:"metricsPort"`

	// Webhook
	// +optional
	// +kubebuilder:default=9443
	WebhookPort int `yaml:"webhook_port" json:"webhookPort"`
	// +optional
	// +kubebuilder:default="/tmp/k8s-webhook-server/serving-certs/tls.crt"
	WebhookCertPath string `yaml:"webhook_cert_path" json:"webhookCertPath"`
	// +optional
	// +kubebuilder:default="/tmp/k8s-webhook-server/serving-certs/tls.key"
	WebhookKeyPath string `yaml:"webhook_key_path" json:"webhookKeyPath"`

	// Sandbox
	// +optional
	// +kubebuilder:default="0s"
	DefaultSandboxTTL metav1.Duration `yaml:"default_sandbox_ttl" json:"defaultSandboxTTL"`
	// +optional
	// +kubebuilder:default="4Gi"
	TeamTemplateMemoryPerCPU string `yaml:"team_template_memory_per_cpu" json:"teamTemplateMemoryPerCpu"`
	// +optional
	SandboxRuntimeClassName string `yaml:"sandbox_runtime_class_name" json:"sandboxRuntimeClassName"`

	// NetworkPolicyProvider selects the dataplane integration used to enforce sandbox network policy.
	// +optional
	// +kubebuilder:default="noop"
	NetworkPolicyProvider string `yaml:"network_policy_provider" json:"-"`

	// Netd apply wait
	// +optional
	// +kubebuilder:default="30s"
	NetdPolicyApplyTimeout metav1.Duration `yaml:"netd_policy_apply_timeout" json:"netdPolicyApplyTimeout"`
	// +optional
	// +kubebuilder:default="500ms"
	NetdPolicyApplyPollInterval metav1.Duration `yaml:"netd_policy_apply_poll_interval" json:"netdPolicyApplyPollInterval"`
	// +optional
	NetdMITMCASecretName string `yaml:"netd_mitm_ca_secret_name" json:"-"`
	// +optional
	NetdMITMCASecretNamespace string `yaml:"netd_mitm_ca_secret_namespace" json:"-"`

	// Pause/Resume
	// +optional
	// +kubebuilder:default="10Mi"
	PauseMinMemoryRequest string `yaml:"pause_min_memory_request" json:"pauseMinMemoryRequest"`
	// +optional
	// +kubebuilder:default="32Mi"
	PauseMinMemoryLimit string `yaml:"pause_min_memory_limit" json:"pauseMinMemoryLimit"`
	// +optional
	// +kubebuilder:default="1.1"
	PauseMemoryBufferRatio string `yaml:"pause_memory_buffer_ratio" json:"pauseMemoryBufferRatio"`
	// +optional
	// +kubebuilder:default="10m"
	PauseMinCPU string `yaml:"pause_min_cpu" json:"pauseMinCPU"`
	// +optional
	// +kubebuilder:default=false
	CtldEnabled bool `yaml:"ctld_enabled" json:"-"`
	// +optional
	// +kubebuilder:default=8095
	CtldPort int `yaml:"ctld_port" json:"-"`

	// Timeouts
	// +optional
	// +kubebuilder:default="30s"
	ProcdClientTimeout metav1.Duration `yaml:"procd_client_timeout" json:"procdClientTimeout"`
	// +optional
	// +kubebuilder:default="15s"
	CtldClientTimeout metav1.Duration `yaml:"ctld_client_timeout" json:"-"`
	// +optional
	// +kubebuilder:default="6s"
	ProcdInitTimeout metav1.Duration `yaml:"procd_init_timeout" json:"procdInitTimeout"`
	// +optional
	// +kubebuilder:default="30s"
	ShutdownTimeout metav1.Duration `yaml:"shutdown_timeout" json:"shutdownTimeout"`

	// Procd config injected into sandbox pods
	// +optional
	// +kubebuilder:default={}
	ProcdConfig          ProcdConfig `yaml:"procd_config" json:"procdConfig"`
	StorageProxyBaseURL  string      `yaml:"storage_proxy_base_url" json:"-"`
	StorageProxyHTTPPort int         `yaml:"storage_proxy_http_port" json:"-"`

	// Registry config for image pull secret provisioning
	// +optional
	// +kubebuilder:default={}
	Registry RegistryConfig `yaml:"registry" json:"-"`

	// Public Exposure config for generating public URLs (injected by infra-operator)
	// +optional
	PublicRootDomain string `yaml:"public_root_domain" json:"-"`
	// +optional
	PublicRegionID string `yaml:"public_region_id" json:"-"`

	// Runtime egress auth resolver settings injected by infra-operator.
	// Resolution now runs inside manager rather than a standalone service.
	// +optional
	EgressAuthDefaultResolveTTL metav1.Duration `yaml:"egress_auth_default_resolve_ttl" json:"-"`
	// +optional
	EgressAuthStaticAuth []StaticEgressAuthConfig `yaml:"egress_auth_static_auth" json:"-"`

	// Autoscaler config for pool scaling behavior
	// +optional
	// +kubebuilder:default={}
	Autoscaler AutoscalerConfig `yaml:"autoscaler" json:"autoscaler"`

	// SandboxPodPlacement configures default scheduling constraints injected into
	// sandbox/template pool pods by the manager. This is operator-populated and
	// hidden from the public CRD surface so placement can follow data-plane
	// topology without requiring users to duplicate netd settings per template.
	// +optional
	SandboxPodPlacement SandboxPodPlacementConfig `yaml:"sandbox_pod_placement" json:"-"`
}

// StaticEgressAuthConfig defines a static auth directive for runtime egress auth injection.
type StaticEgressAuthConfig struct {
	AuthRef string            `yaml:"auth_ref" json:"authRef"`
	Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
	TTL     metav1.Duration   `yaml:"ttl" json:"ttl"`
}

// SandboxPodPlacementConfig defines default scheduling constraints for sandbox
// workloads generated by the manager.
type SandboxPodPlacementConfig struct {
	NodeSelector map[string]string   `yaml:"node_selector" json:"-"`
	Tolerations  []corev1.Toleration `yaml:"tolerations" json:"-"`
}

// AutoscalerConfig holds autoscaler settings for pool scaling behavior.
type AutoscalerConfig struct {
	// MinScaleInterval is the minimum time between scale operations for a template.
	// This prevents thundering herd when multiple cold claims arrive simultaneously.
	// +optional
	// +kubebuilder:default="100ms"
	MinScaleInterval metav1.Duration `yaml:"min_scale_interval" json:"minScaleInterval"`

	// ScaleUpFactor determines how aggressively to scale up.
	// When cold claim occurs, newReplicas = current * ScaleUpFactor.
	// +optional
	// +kubebuilder:default="1.5"
	ScaleUpFactor string `yaml:"scale_up_factor" json:"scaleUpFactor"`

	// MaxScaleStep caps the maximum pods to add in a single scale operation.
	// +optional
	// +kubebuilder:default=10
	MaxScaleStep int32 `yaml:"max_scale_step" json:"maxScaleStep"`

	// MinIdleBuffer is the minimum number of idle pods to maintain above minIdle.
	// When idle count drops to minIdle + MinIdleBuffer, proactive scaling kicks in.
	// +optional
	// +kubebuilder:default=2
	MinIdleBuffer int32 `yaml:"min_idle_buffer" json:"minIdleBuffer"`

	// TargetIdleRatio is the target ratio of idle pods to active pods.
	// Formula: desiredIdle = active * TargetIdleRatio
	// +optional
	// +kubebuilder:default="0.2"
	TargetIdleRatio string `yaml:"target_idle_ratio" json:"targetIdleRatio"`

	// NoTrafficScaleDownAfter is the duration without any claims before scaling down.
	// Scale down is still async and happens in the background reconcile loop.
	// +optional
	// +kubebuilder:default="10m"
	NoTrafficScaleDownAfter metav1.Duration `yaml:"no_traffic_scale_down_after" json:"noTrafficScaleDownAfter"`

	// ScaleDownPercent is the percentage to reduce replicas during scale down.
	// +optional
	// +kubebuilder:default="0.1"
	ScaleDownPercent string `yaml:"scale_down_percent" json:"scaleDownPercent"`
}

func (c AutoscalerConfig) ParsedScaleUpFactor(defaultValue float64) float64 {
	return parseFloatOrDefault(c.ScaleUpFactor, defaultValue)
}

func (c AutoscalerConfig) ParsedTargetIdleRatio(defaultValue float64) float64 {
	return parseFloatOrDefault(c.TargetIdleRatio, defaultValue)
}

func (c AutoscalerConfig) ParsedScaleDownPercent(defaultValue float64) float64 {
	return parseFloatOrDefault(c.ScaleDownPercent, defaultValue)
}

func parseFloatOrDefault(value string, defaultValue float64) float64 {
	if value == "" {
		return defaultValue
	}
	v, err := strconv.ParseFloat(value, 64)
	if err != nil {
		return defaultValue
	}
	return v
}

// RegistryConfig holds registry settings for manager.
type RegistryConfig struct {
	// Provider is the registry provider identifier.
	// +optional
	Provider string `yaml:"provider" json:"-"`

	// PushRegistry is the registry hostname (host:port) for external image push.
	// +optional
	PushRegistry string `yaml:"push_registry" json:"-"`

	// PullRegistry is the registry hostname (host:port) for in-cluster image pull.
	// +optional
	PullRegistry string `yaml:"pull_registry" json:"-"`

	// PullSecretName is the secret name to create in template namespaces.
	// +optional
	PullSecretName string `yaml:"pull_secret_name" json:"-"`

	// PullCredentialsFile is the path to dockerconfigjson file for pull secrets.
	// +optional
	PullCredentialsFile string `yaml:"pull_credentials_file" json:"-"`

	// Namespace is the namespace for registry secrets.
	// +optional
	Namespace string `yaml:"namespace" json:"-"`

	// AWS configures AWS registry integration.
	// +optional
	AWS *RegistryAWSConfig `yaml:"aws" json:"-"`

	// GCP configures GCP registry integration.
	// +optional
	GCP *RegistryGCPConfig `yaml:"gcp" json:"-"`

	// Azure configures Azure registry integration.
	// +optional
	Azure *RegistryAzureConfig `yaml:"azure" json:"-"`

	// Aliyun configures Aliyun registry integration.
	// +optional
	Aliyun *RegistryAliyunConfig `yaml:"aliyun" json:"-"`

	// Harbor configures Harbor registry integration.
	// +optional
	Harbor *RegistryHarborConfig `yaml:"harbor" json:"-"`

	// Builtin configures builtin registry integration.
	// +optional
	Builtin *RegistryBuiltinConfig `yaml:"builtin" json:"-"`
}

// RegistryAWSConfig defines AWS registry config.
type RegistryAWSConfig struct {
	Region           string `yaml:"region" json:"-"`
	RegistryID       string `yaml:"registry_id" json:"-"`
	AssumeRoleARN    string `yaml:"assume_role_arn" json:"-"`
	ExternalID       string `yaml:"external_id" json:"-"`
	AccessKeySecret  string `yaml:"access_key_secret" json:"-"`
	AccessKeyKey     string `yaml:"access_key_key" json:"-"`
	SecretKeyKey     string `yaml:"secret_key_key" json:"-"`
	SessionTokenKey  string `yaml:"session_token_key" json:"-"`
	RegistryOverride string `yaml:"registry_override" json:"-"`
	AccessKeyID      string `yaml:"access_key_id" json:"-"`
	SecretAccessKey  string `yaml:"secret_access_key" json:"-"`
	SessionToken     string `yaml:"session_token" json:"-"`
}

// RegistryGCPConfig defines GCP registry config.
type RegistryGCPConfig struct {
	Registry             string `yaml:"registry" json:"-"`
	ServiceAccountSecret string `yaml:"service_account_secret" json:"-"`
	ServiceAccountKey    string `yaml:"service_account_key" json:"-"`
	ServiceAccountJSON   string `yaml:"service_account_json" json:"-"`
}

// RegistryAzureConfig defines Azure registry config.
type RegistryAzureConfig struct {
	Registry          string `yaml:"registry" json:"-"`
	CredentialsSecret string `yaml:"credentials_secret" json:"-"`
	TenantIDKey       string `yaml:"tenant_id_key" json:"-"`
	ClientIDKey       string `yaml:"client_id_key" json:"-"`
	ClientSecretKey   string `yaml:"client_secret_key" json:"-"`
	TenantID          string `yaml:"tenant_id" json:"-"`
	ClientID          string `yaml:"client_id" json:"-"`
	ClientSecret      string `yaml:"client_secret" json:"-"`
}

// RegistryAliyunConfig defines Aliyun registry config.
type RegistryAliyunConfig struct {
	Registry          string `yaml:"registry" json:"-"`
	Region            string `yaml:"region" json:"-"`
	InstanceID        string `yaml:"instance_id" json:"-"`
	CredentialsSecret string `yaml:"credentials_secret" json:"-"`
	AccessKeyKey      string `yaml:"access_key_key" json:"-"`
	SecretKeyKey      string `yaml:"secret_key_key" json:"-"`
	AccessKeyID       string `yaml:"access_key_id" json:"-"`
	AccessKeySecret   string `yaml:"access_key_secret" json:"-"`
}

// RegistryHarborConfig defines Harbor registry config.
type RegistryHarborConfig struct {
	Registry          string `yaml:"registry" json:"-"`
	CredentialsSecret string `yaml:"credentials_secret" json:"-"`
	UsernameKey       string `yaml:"username_key" json:"-"`
	PasswordKey       string `yaml:"password_key" json:"-"`
	Username          string `yaml:"username" json:"-"`
	Password          string `yaml:"password" json:"-"`
}

// RegistryBuiltinConfig defines builtin registry config.
type RegistryBuiltinConfig struct {
	// AuthSecretName is the name of the secret containing registry auth credentials.
	AuthSecretName string `yaml:"auth_secret_name" json:"-"`
	// UsernameKey is the key for username in the auth secret.
	UsernameKey string `yaml:"username_key" json:"-"`
	// PasswordKey is the key for password in the auth secret.
	PasswordKey string `yaml:"password_key" json:"-"`
	// Username is the direct registry username. If set, AuthSecretName is not required.
	Username string `yaml:"username" json:"-"`
	// Password is the direct registry password. If set, AuthSecretName is not required.
	Password string `yaml:"password" json:"-"`
}

// LoadManagerConfig returns the manager configuration.
func LoadManagerConfig() *ManagerConfig {
	path := os.Getenv("CONFIG_PATH")
	if path == "" {
		path = "/config/config.yaml"
	}

	cfg, err := loadManagerConfig(path)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load config from %s: %v, using empty config\n", path, err)
		cfg = &ManagerConfig{}
	}
	if cfg.EgressAuthDefaultResolveTTL.Duration == 0 {
		cfg.EgressAuthDefaultResolveTTL = metav1.Duration{Duration: 5 * time.Minute}
	}
	for idx := range cfg.EgressAuthStaticAuth {
		if cfg.EgressAuthStaticAuth[idx].TTL.Duration == 0 {
			cfg.EgressAuthStaticAuth[idx].TTL = cfg.EgressAuthDefaultResolveTTL
		}
	}
	return cfg
}

func loadManagerConfig(path string) (*ManagerConfig, error) {
	cfg := &ManagerConfig{}
	if path == "" {
		return cfg, nil
	}

	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("failed to read config file: %w", err)
	}

	// Expand environment variables
	data = []byte(os.ExpandEnv(string(data)))

	if err := yaml.Unmarshal(data, cfg); err != nil {
		return nil, fmt.Errorf("failed to unmarshal config: %w", err)
	}

	return cfg, nil
}
