{{/*
Expand the name of the chart.
*/}}
{{- define "shenron-dynamo.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a fully qualified app name.
*/}}
{{- define "shenron-dynamo.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := include "shenron-dynamo.name" . -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Chart metadata label.
*/}}
{{- define "shenron-dynamo.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Common labels.
*/}}
{{- define "shenron-dynamo.labels" -}}
helm.sh/chart: {{ include "shenron-dynamo.chart" . }}
app.kubernetes.io/name: {{ include "shenron-dynamo.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}

{{/*
Labels used to select pods.
*/}}
{{- define "shenron-dynamo.selectorLabels" -}}
app.kubernetes.io/name: {{ include "shenron-dynamo.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}

{{/*
Sanitize an arbitrary model key into a DNS-safe slug.
*/}}
{{- define "shenron-dynamo.modelSlug" -}}
{{- $name := printf "%v" . -}}
{{- $slug := trimAll "-" (regexReplaceAll "[^a-z0-9]+" (lower $name) "-") -}}
{{- if eq $slug "" -}}
model
{{- else -}}
{{- $slug -}}
{{- end -}}
{{- end -}}

{{/*
Stable short hash for model keys.
*/}}
{{- define "shenron-dynamo.modelHash" -}}
{{- sha256sum (printf "%v" .) | trunc 8 -}}
{{- end -}}

{{/*
Stable model id for labels.
*/}}
{{- define "shenron-dynamo.modelID" -}}
{{- $slug := include "shenron-dynamo.modelSlug" . -}}
{{- $hash := include "shenron-dynamo.modelHash" . -}}
{{- $maxSlugLen := int (sub 63 (add 1 (len $hash))) -}}
{{- $trimmedSlug := trunc $maxSlugLen $slug | trimSuffix "-" -}}
{{- printf "%s-%s" $trimmedSlug $hash | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Resource name per model, unique and stable.
*/}}
{{- define "shenron-dynamo.modelResourceName" -}}
{{- $root := index . "root" -}}
{{- $modelName := index . "modelName" -}}
{{- $fullname := include "shenron-dynamo.fullname" $root -}}
{{- $modelID := include "shenron-dynamo.modelID" $modelName -}}
{{- $maxPrefixLen := int (sub 63 (add 1 (len $modelID))) -}}
{{- $trimmedPrefix := trunc $maxPrefixLen $fullname | trimSuffix "-" -}}
{{- printf "%s-%s" $trimmedPrefix $modelID | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Resource name per worker replica.
*/}}
{{- define "shenron-dynamo.workerResourceName" -}}
{{- $root := index . "root" -}}
{{- $modelName := index . "modelName" -}}
{{- $replica := int (index . "replica") -}}
{{- $base := include "shenron-dynamo.modelResourceName" (dict "root" $root "modelName" $modelName) -}}
{{- $suffix := printf "r%d" $replica -}}
{{- $maxBaseLen := int (sub 45 (add 1 (len $suffix))) -}}
{{- printf "%s-%s" (trunc $maxBaseLen $base | trimSuffix "-") $suffix | trunc 45 | trimSuffix "-" -}}
{{- end -}}

{{/*
Short service key for worker components inside a DynamoGraphDeployment.
Must stay short enough for operator Grove naming limits.
*/}}
{{- define "shenron-dynamo.workerComponentName" -}}
{{- $modelName := index . "modelName" -}}
{{- $replica := int (index . "replica") -}}
{{- $modelSlug := include "shenron-dynamo.modelSlug" $modelName -}}
{{- $modelHash := include "shenron-dynamo.modelHash" $modelName -}}
{{- $replicaStr := printf "%d" $replica -}}
{{- $maxSlugLen := int (sub 45 (add 13 (len $replicaStr))) -}}
{{- if lt $maxSlugLen 1 -}}
{{- $maxSlugLen = 1 -}}
{{- end -}}
{{- $trimmedSlug := trunc $maxSlugLen $modelSlug | trimSuffix "-" -}}
{{- printf "w%s-%s-r%s" $trimmedSlug $modelHash $replicaStr | trunc 45 | trimSuffix "-" -}}
{{- end -}}

{{/*
Validate top-level models map.
*/}}
{{- define "shenron-dynamo.validateModelsMap" -}}
{{- if not (kindIs "map" .Values.models) -}}
{{- fail "values.models must be a map keyed by model name" -}}
{{- end -}}
{{- end -}}

{{/*
Resolve and validate Caddy enabled flag.
*/}}
{{- define "shenron-dynamo.caddyEnabled" -}}
{{- $caddy := .Values.caddy | default dict -}}
{{- $enabled := true -}}
{{- if hasKey $caddy "enabled" -}}
{{- $enabled = get $caddy "enabled" -}}
{{- end -}}
{{- if not (kindIs "bool" $enabled) -}}
{{- fail "values.caddy.enabled must be a boolean" -}}
{{- end -}}
{{- $enabled -}}
{{- end -}}

{{/*
Resolve Caddy FQDN. Returns empty string when not set.
*/}}
{{- define "shenron-dynamo.caddyFqdn" -}}
{{- $caddy := .Values.caddy | default dict -}}
{{- $fqdn := "" -}}
{{- if hasKey $caddy "fqdn" -}}
{{- $fqdn = printf "%v" (get $caddy "fqdn") -}}
{{- end -}}
{{- trim $fqdn -}}
{{- end -}}

{{/*
Resource name for Caddy Deployment/Service/ConfigMap.
*/}}
{{- define "shenron-dynamo.caddyName" -}}
{{- printf "%s-caddy" (include "shenron-dynamo.fullname" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Resolve and validate Hugging Face cache enabled flag.
*/}}
{{- define "shenron-dynamo.hfCacheEnabled" -}}
{{- $cache := .Values.huggingfaceCache | default dict -}}
{{- $enabled := true -}}
{{- if hasKey $cache "enabled" -}}
{{- $enabled = get $cache "enabled" -}}
{{- end -}}
{{- if not (kindIs "bool" $enabled) -}}
{{- fail "values.huggingfaceCache.enabled must be a boolean" -}}
{{- end -}}
{{- $enabled -}}
{{- end -}}

{{/*
Resolve and validate Hugging Face cache host path.
*/}}
{{- define "shenron-dynamo.hfCacheHostPath" -}}
{{- $cache := .Values.huggingfaceCache | default dict -}}
{{- $path := "/root/.cache/huggingface" -}}
{{- if hasKey $cache "hostPath" -}}
{{- $path = trim (printf "%v" (get $cache "hostPath")) -}}
{{- end -}}
{{- if eq $path "" -}}
{{- fail "values.huggingfaceCache.hostPath must not be empty" -}}
{{- end -}}
{{- if not (hasPrefix "/" $path) -}}
{{- fail "values.huggingfaceCache.hostPath must be an absolute path (do not use ~)" -}}
{{- end -}}
{{- $path -}}
{{- end -}}

{{/*
Resolve and validate Hugging Face cache mount path.
*/}}
{{- define "shenron-dynamo.hfCacheMountPath" -}}
{{- $cache := .Values.huggingfaceCache | default dict -}}
{{- $path := "/root/.cache/huggingface" -}}
{{- if hasKey $cache "mountPath" -}}
{{- $path = trim (printf "%v" (get $cache "mountPath")) -}}
{{- end -}}
{{- if eq $path "" -}}
{{- fail "values.huggingfaceCache.mountPath must not be empty" -}}
{{- end -}}
{{- if not (hasPrefix "/" $path) -}}
{{- fail "values.huggingfaceCache.mountPath must be an absolute path" -}}
{{- end -}}
{{- $path -}}
{{- end -}}

{{/*
Resolve and validate Hugging Face cache hostPath type.
*/}}
{{- define "shenron-dynamo.hfCacheHostPathType" -}}
{{- $cache := .Values.huggingfaceCache | default dict -}}
{{- $t := "DirectoryOrCreate" -}}
{{- if hasKey $cache "hostPathType" -}}
{{- $t = trim (printf "%v" (get $cache "hostPathType")) -}}
{{- end -}}
{{- if not (or (eq $t "DirectoryOrCreate") (eq $t "Directory") (eq $t "FileOrCreate") (eq $t "File") (eq $t "Socket") (eq $t "CharDevice") (eq $t "BlockDevice")) -}}
{{- fail "values.huggingfaceCache.hostPathType must be a valid Kubernetes hostPath type" -}}
{{- end -}}
{{- $t -}}
{{- end -}}

{{/*
Validate Hugging Face cache settings.
*/}}
{{- define "shenron-dynamo.validateHuggingfaceCache" -}}
{{- $_ := include "shenron-dynamo.hfCacheEnabled" . -}}
{{- $_ := include "shenron-dynamo.hfCacheHostPath" . -}}
{{- $_ := include "shenron-dynamo.hfCacheMountPath" . -}}
{{- $_ := include "shenron-dynamo.hfCacheHostPathType" . -}}
{{- end -}}

{{/*
Validate per-model required fields and types.
*/}}
{{- define "shenron-dynamo.validateModel" -}}
{{- $modelName := index . "modelName" -}}
{{- $model := index . "model" -}}
{{- if not (kindIs "map" $model) -}}
{{- fail (printf "models.%s must be an object" $modelName) -}}
{{- end -}}
{{- if not (hasKey $model "replicas") -}}
{{- fail (printf "models.%s.replicas is required" $modelName) -}}
{{- end -}}
{{- if not (hasKey $model "command") -}}
{{- fail (printf "models.%s.command is required" $modelName) -}}
{{- end -}}
{{- $replicas := int (get $model "replicas") -}}
{{- if lt $replicas 0 -}}
{{- fail (printf "models.%s.replicas must be >= 0" $modelName) -}}
{{- end -}}
{{- $command := get $model "command" -}}
{{- if not (kindIs "slice" $command) -}}
{{- fail (printf "models.%s.command must be a list of command arguments" $modelName) -}}
{{- end -}}
{{- if eq (len $command) 0 -}}
{{- fail (printf "models.%s.command must not be empty" $modelName) -}}
{{- end -}}
{{- if has "--port" $command -}}
{{- fail (printf "models.%s.command must not include --port; worker ports are chart-managed" $modelName) -}}
{{- end -}}
{{- if hasKey $model "args" -}}
{{- $args := get $model "args" -}}
{{- if not (kindIs "slice" $args) -}}
{{- fail (printf "models.%s.args must be a list of command arguments" $modelName) -}}
{{- end -}}
{{- if has "--port" $args -}}
{{- fail (printf "models.%s.args must not include --port; worker ports are chart-managed" $modelName) -}}
{{- end -}}
{{- end -}}
{{- if hasKey $model "num_gpus" -}}
{{- $numGpusRaw := printf "%v" (get $model "num_gpus") -}}
{{- if not (regexMatch "^[0-9]+$" $numGpusRaw) -}}
{{- fail (printf "models.%s.num_gpus must be a non-negative integer" $modelName) -}}
{{- end -}}
{{- end -}}
{{- if hasKey $model "env" -}}
{{- $env := get $model "env" -}}
{{- if not (kindIs "slice" $env) -}}
{{- fail (printf "models.%s.env must be a list of {name, value} objects" $modelName) -}}
{{- end -}}
{{- range $idx, $item := $env -}}
{{- if not (kindIs "map" $item) -}}
{{- fail (printf "models.%s.env[%d] must be an object with name and value" $modelName $idx) -}}
{{- end -}}
{{- if not (hasKey $item "name") -}}
{{- fail (printf "models.%s.env[%d].name is required" $modelName $idx) -}}
{{- end -}}
{{- if not (hasKey $item "value") -}}
{{- fail (printf "models.%s.env[%d].value is required" $modelName $idx) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if hasKey $model "image" -}}
{{- $image := get $model "image" -}}
{{- if kindIs "map" $image -}}
{{- else if kindIs "string" $image -}}
{{- if eq (trim $image) "" -}}
{{- fail (printf "models.%s.image must not be empty" $modelName) -}}
{{- end -}}
{{- else -}}
{{- fail (printf "models.%s.image must be either a string image reference or an object" $modelName) -}}
{{- end -}}
{{- end -}}
{{- if hasKey $model "shm" -}}
{{- $shm := get $model "shm" -}}
{{- if not (kindIs "map" $shm) -}}
{{- fail (printf "models.%s.shm must be an object" $modelName) -}}
{{- end -}}
{{- end -}}
{{- if hasKey $model "resources" -}}
{{- $resources := get $model "resources" -}}
{{- if not (kindIs "map" $resources) -}}
{{- fail (printf "models.%s.resources must be an object" $modelName) -}}
{{- end -}}
{{- if hasKey $model "num_gpus" -}}
{{- $limits := get $resources "limits" | default dict -}}
{{- if and (kindIs "map" $limits) (hasKey $limits "nvidia.com/gpu") -}}
{{- fail (printf "models.%s.resources.limits.nvidia.com/gpu must not be set when models.%s.num_gpus is set" $modelName $modelName) -}}
{{- end -}}
{{- $requests := get $resources "requests" | default dict -}}
{{- if and (kindIs "map" $requests) (hasKey $requests "nvidia.com/gpu") -}}
{{- fail (printf "models.%s.resources.requests.nvidia.com/gpu must not be set when models.%s.num_gpus is set" $modelName $modelName) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Resolve a model image reference. Accepts either:
- models.<name>.image: "repo:tag"
- models.<name>.image.repository/tag[/pullPolicy]
Falls back to top-level image.repository/tag/pullPolicy.
*/}}
{{- define "shenron-dynamo.resolveModelImage" -}}
{{- $modelName := index . "modelName" -}}
{{- $model := index . "model" -}}
{{- $globalImage := index . "globalImage" | default dict -}}
{{- $imageValue := get $model "image" -}}
{{- $repository := "" -}}
{{- $tag := "" -}}
{{- $pullPolicy := coalesce (get $globalImage "pullPolicy") "IfNotPresent" -}}
{{- if hasKey $model "image" -}}
{{- if kindIs "string" $imageValue -}}
{{- $raw := trim $imageValue -}}
{{- $parts := regexSplit ":" $raw 2 -}}
{{- if ne (len $parts) 2 -}}
{{- fail (printf "models.%s.image as a string must be in repository:tag format" $modelName) -}}
{{- end -}}
{{- $repository = trim (index $parts 0) -}}
{{- $tag = trim (index $parts 1) -}}
{{- else -}}
{{- $repository = trim (printf "%v" (coalesce (get $imageValue "repository") "")) -}}
{{- $tag = trim (printf "%v" (coalesce (get $imageValue "tag") "")) -}}
{{- $pullPolicy = coalesce (get $imageValue "pullPolicy") $pullPolicy -}}
{{- end -}}
{{- end -}}
{{- if eq $repository "" -}}
{{- $repository = trim (printf "%v" (coalesce (get $globalImage "repository") "")) -}}
{{- end -}}
{{- if eq $tag "" -}}
{{- $tag = trim (printf "%v" (coalesce (get $globalImage "tag") "")) -}}
{{- end -}}
{{- if eq $repository "" -}}
{{- fail (printf "models.%s image repository is required (set models.%s.image or models.%s.image.repository or image.repository)" $modelName $modelName $modelName) -}}
{{- end -}}
{{- if eq $tag "" -}}
{{- fail (printf "models.%s image tag is required (set models.%s.image as repository:tag, models.%s.image.tag, or image.tag)" $modelName $modelName $modelName) -}}
{{- end -}}
repository: {{ $repository | quote }}
tag: {{ $tag | quote }}
pullPolicy: {{ printf "%v" $pullPolicy | quote }}
{{- end -}}

{{/*
Resolve and validate cluster.total_gpus.
*/}}
{{- define "shenron-dynamo.clusterTotalGpus" -}}
{{- $cluster := .Values.cluster | default dict -}}
{{- if not (kindIs "map" $cluster) -}}
{{- fail "values.cluster must be an object" -}}
{{- end -}}
{{- if not (hasKey $cluster "total_gpus") -}}
{{- fail "values.cluster.total_gpus is required" -}}
{{- end -}}
{{- $totalRaw := printf "%v" (get $cluster "total_gpus") -}}
{{- if not (regexMatch "^[0-9]+$" $totalRaw) -}}
{{- fail "values.cluster.total_gpus must be a non-negative integer" -}}
{{- end -}}
{{- int $totalRaw -}}
{{- end -}}

{{/*
Validate cluster values.
*/}}
{{- define "shenron-dynamo.validateCluster" -}}
{{- $_ := include "shenron-dynamo.clusterTotalGpus" . -}}
{{- end -}}

{{/*
Resolve and validate replica manager enabled flag.
*/}}
{{- define "shenron-dynamo.replicaManagerEnabled" -}}
{{- $manager := .Values.replicaManager | default dict -}}
{{- if not (kindIs "map" $manager) -}}
{{- fail "values.replicaManager must be an object" -}}
{{- end -}}
{{- $enabled := true -}}
{{- if hasKey $manager "enabled" -}}
{{- $enabled = get $manager "enabled" -}}
{{- end -}}
{{- if not (kindIs "bool" $enabled) -}}
{{- fail "values.replicaManager.enabled must be a boolean" -}}
{{- end -}}
{{- $enabled -}}
{{- end -}}

{{/*
Resource name for replica manager Deployment/Service.
*/}}
{{- define "shenron-dynamo.replicaManagerName" -}}
{{- printf "%s-replica-manager" (include "shenron-dynamo.fullname" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Service account name for replica manager.
*/}}
{{- define "shenron-dynamo.replicaManagerServiceAccountName" -}}
{{- include "shenron-dynamo.replicaManagerName" . -}}
{{- end -}}

{{/*
Resolve and validate replica manager Deployment replicas.
*/}}
{{- define "shenron-dynamo.replicaManagerReplicas" -}}
{{- $manager := .Values.replicaManager | default dict -}}
{{- $replicas := 1 -}}
{{- if hasKey $manager "replicas" -}}
{{- $replicas = int (get $manager "replicas") -}}
{{- end -}}
{{- if lt $replicas 0 -}}
{{- fail "values.replicaManager.replicas must be >= 0" -}}
{{- end -}}
{{- $replicas -}}
{{- end -}}

{{/*
Resolve and validate replica manager container listen port.
*/}}
{{- define "shenron-dynamo.replicaManagerPort" -}}
{{- $manager := .Values.replicaManager | default dict -}}
{{- $port := 8081 -}}
{{- if hasKey $manager "port" -}}
{{- $port = int (get $manager "port") -}}
{{- end -}}
{{- if or (lt $port 1) (gt $port 65535) -}}
{{- fail "values.replicaManager.port must be between 1 and 65535" -}}
{{- end -}}
{{- $port -}}
{{- end -}}

{{/*
Resolve and validate replica manager service port.
*/}}
{{- define "shenron-dynamo.replicaManagerServicePort" -}}
{{- $manager := .Values.replicaManager | default dict -}}
{{- $service := get $manager "service" | default dict -}}
{{- $port := int (include "shenron-dynamo.replicaManagerPort" .) -}}
{{- if hasKey $service "port" -}}
{{- $port = int (get $service "port") -}}
{{- end -}}
{{- if or (lt $port 1) (gt $port 65535) -}}
{{- fail "values.replicaManager.service.port must be between 1 and 65535" -}}
{{- end -}}
{{- $port -}}
{{- end -}}

{{/*
Resolve and validate replica manager auth secret name.
*/}}
{{- define "shenron-dynamo.replicaManagerTokenSecretName" -}}
{{- $manager := .Values.replicaManager | default dict -}}
{{- $auth := get $manager "auth" | default dict -}}
{{- $tokenSecret := get $auth "tokenSecret" | default dict -}}
{{- $name := "replica-manager-auth" -}}
{{- if hasKey $tokenSecret "name" -}}
{{- $name = trim (printf "%v" (get $tokenSecret "name")) -}}
{{- end -}}
{{- if eq $name "" -}}
{{- fail "values.replicaManager.auth.tokenSecret.name must not be empty" -}}
{{- end -}}
{{- $name -}}
{{- end -}}

{{/*
Resolve and validate replica manager auth secret key.
*/}}
{{- define "shenron-dynamo.replicaManagerTokenSecretKey" -}}
{{- $manager := .Values.replicaManager | default dict -}}
{{- $auth := get $manager "auth" | default dict -}}
{{- $tokenSecret := get $auth "tokenSecret" | default dict -}}
{{- $key := "token" -}}
{{- if hasKey $tokenSecret "key" -}}
{{- $key = trim (printf "%v" (get $tokenSecret "key")) -}}
{{- end -}}
{{- if eq $key "" -}}
{{- fail "values.replicaManager.auth.tokenSecret.key must not be empty" -}}
{{- end -}}
{{- $key -}}
{{- end -}}

{{/*
Resolve and validate replica manager Helm release name.
*/}}
{{- define "shenron-dynamo.replicaManagerHelmReleaseName" -}}
{{- $manager := .Values.replicaManager | default dict -}}
{{- $helm := get $manager "helm" | default dict -}}
{{- $releaseName := .Release.Name -}}
{{- if hasKey $helm "releaseName" -}}
{{- $candidate := trim (printf "%v" (get $helm "releaseName")) -}}
{{- if ne $candidate "" -}}
{{- $releaseName = $candidate -}}
{{- end -}}
{{- end -}}
{{- if eq $releaseName "" -}}
{{- fail "values.replicaManager.helm.releaseName must not be empty" -}}
{{- end -}}
{{- $releaseName -}}
{{- end -}}

{{/*
Resolve and validate replica manager Helm namespace.
*/}}
{{- define "shenron-dynamo.replicaManagerHelmNamespace" -}}
{{- $manager := .Values.replicaManager | default dict -}}
{{- $helm := get $manager "helm" | default dict -}}
{{- $namespace := .Release.Namespace -}}
{{- if hasKey $helm "namespace" -}}
{{- $candidate := trim (printf "%v" (get $helm "namespace")) -}}
{{- if ne $candidate "" -}}
{{- $namespace = $candidate -}}
{{- end -}}
{{- end -}}
{{- if eq $namespace "" -}}
{{- fail "values.replicaManager.helm.namespace must not be empty" -}}
{{- end -}}
{{- $namespace -}}
{{- end -}}

{{/*
Resolve and validate replica manager chart path.
*/}}
{{- define "shenron-dynamo.replicaManagerHelmChartPath" -}}
{{- $manager := .Values.replicaManager | default dict -}}
{{- $helm := get $manager "helm" | default dict -}}
{{- $chartPath := "/opt/shenron/helm/dynamo" -}}
{{- if hasKey $helm "chartPath" -}}
{{- $chartPath = trim (printf "%v" (get $helm "chartPath")) -}}
{{- end -}}
{{- if eq $chartPath "" -}}
{{- fail "values.replicaManager.helm.chartPath must not be empty" -}}
{{- end -}}
{{- $chartPath -}}
{{- end -}}

{{/*
Validate replica manager settings.
*/}}
{{- define "shenron-dynamo.validateReplicaManager" -}}
{{- $_ := include "shenron-dynamo.replicaManagerEnabled" . -}}
{{- $_ := include "shenron-dynamo.replicaManagerReplicas" . -}}
{{- $_ := include "shenron-dynamo.replicaManagerPort" . -}}
{{- $_ := include "shenron-dynamo.replicaManagerServicePort" . -}}
{{- $_ := include "shenron-dynamo.replicaManagerTokenSecretName" . -}}
{{- $_ := include "shenron-dynamo.replicaManagerTokenSecretKey" . -}}
{{- $_ := include "shenron-dynamo.replicaManagerHelmReleaseName" . -}}
{{- $_ := include "shenron-dynamo.replicaManagerHelmNamespace" . -}}
{{- $_ := include "shenron-dynamo.replicaManagerHelmChartPath" . -}}
{{- end -}}

{{/*
Resolve and validate discovery backend.
*/}}
{{- define "shenron-dynamo.discoveryBackend" -}}
{{- $backend := lower (trim (printf "%v" (.Values.discoveryBackend | default "etcd"))) -}}
{{- if eq $backend "" -}}
{{- fail "values.discoveryBackend must not be empty" -}}
{{- end -}}
{{- $backend -}}
{{- end -}}

{{/*
Resolve and validate discovery endpoint.
*/}}
{{- define "shenron-dynamo.discoveryEndpoint" -}}
{{- $endpoint := trim (printf "%v" (.Values.discoveryEndpoint | default "")) -}}
{{- if and (eq (include "shenron-dynamo.discoveryBackend" .) "etcd") (eq $endpoint "") -}}
{{- fail "values.discoveryEndpoint is required when values.discoveryBackend=etcd" -}}
{{- end -}}
{{- $endpoint -}}
{{- end -}}

{{/*
Resolve and validate NATS server.
*/}}
{{- define "shenron-dynamo.natsServer" -}}
{{- $server := trim (printf "%v" (.Values.natsServer | default "")) -}}
{{- if eq $server "" -}}
{{- fail "values.natsServer must not be empty" -}}
{{- end -}}
{{- $server -}}
{{- end -}}

{{/*
Resolve and validate request plane.
*/}}
{{- define "shenron-dynamo.requestPlane" -}}
{{- $plane := lower (trim (printf "%v" (.Values.requestPlane | default "http"))) -}}
{{- if not (or (eq $plane "http") (eq $plane "nats")) -}}
{{- fail "values.requestPlane must be either http or nats" -}}
{{- end -}}
{{- $plane -}}
{{- end -}}

{{/*
Whether the selected request plane needs per-worker HTTP RPC addressing.
*/}}
{{- define "shenron-dynamo.usesHttpRpc" -}}
{{- eq (include "shenron-dynamo.requestPlane" .) "http" -}}
{{- end -}}

{{/*
Resolve and validate worker http host.
*/}}
{{- define "shenron-dynamo.httpHost" -}}
{{- $host := trim (printf "%v" (.Values.httpHost | default "0.0.0.0")) -}}
{{- if eq $host "" -}}
{{- fail "values.httpHost must not be empty" -}}
{{- end -}}
{{- $host -}}
{{- end -}}

{{/*
Resolve and validate worker http port.
*/}}
{{- define "shenron-dynamo.httpPort" -}}
{{- $port := int (.Values.httpPort | default 8888) -}}
{{- if or (lt $port 1) (gt $port 65535) -}}
{{- fail "values.httpPort must be between 1 and 65535" -}}
{{- end -}}
{{- $port -}}
{{- end -}}

{{/*
Count all worker replicas.
*/}}
{{- define "shenron-dynamo.totalWorkerReplicas" -}}
{{- include "shenron-dynamo.validateModelsMap" . -}}
{{- $total := 0 -}}
{{- range $modelName := keys .Values.models | sortAlpha }}
{{- $model := index $.Values.models $modelName -}}
{{- include "shenron-dynamo.validateModel" (dict "modelName" $modelName "model" $model) -}}
{{- $total = add $total (int (get $model "replicas")) -}}
{{- end -}}
{{- $total -}}
{{- end -}}

{{/*
Resolve and validate worker service type.
*/}}
{{- define "shenron-dynamo.workerServiceType" -}}
{{- $cfg := .Values.workerService | default dict -}}
{{- $svcType := trim (printf "%v" (get $cfg "type" | default "NodePort")) -}}
{{- if not (or (eq $svcType "NodePort") (eq $svcType "LoadBalancer") (eq $svcType "ClusterIP")) -}}
{{- fail "values.workerService.type must be NodePort, LoadBalancer, or ClusterIP" -}}
{{- end -}}
{{- $svcType -}}
{{- end -}}

{{/*
Resolve and validate worker service base port.
*/}}
{{- define "shenron-dynamo.workerServiceBasePort" -}}
{{- $cfg := .Values.workerService | default dict -}}
{{- if not (hasKey $cfg "basePort") -}}
{{- fail "values.workerService.basePort is required" -}}
{{- end -}}
{{- $basePort := int (get $cfg "basePort") -}}
{{- $svcType := include "shenron-dynamo.workerServiceType" . -}}
{{- if eq $svcType "NodePort" -}}
{{- if or (lt $basePort 30000) (gt $basePort 32767) -}}
{{- fail "values.workerService.basePort must be between 30000 and 32767 when workerService.type=NodePort" -}}
{{- end -}}
{{- else -}}
{{- if or (lt $basePort 1) (gt $basePort 65535) -}}
{{- fail "values.workerService.basePort must be between 1 and 65535" -}}
{{- end -}}
{{- end -}}
{{- $basePort -}}
{{- end -}}

{{/*
Resolve worker service port name.
*/}}
{{- define "shenron-dynamo.workerServicePortName" -}}
{{- $cfg := .Values.workerService | default dict -}}
{{- $name := trim (printf "%v" (get $cfg "portName" | default "dyn-http")) -}}
{{- if eq $name "" -}}
{{- fail "values.workerService.portName must not be empty" -}}
{{- end -}}
{{- $name -}}
{{- end -}}

{{/*
Validate worker addressing assumptions.
*/}}
{{- define "shenron-dynamo.validateWorkerConfig" -}}
{{- $_ := include "shenron-dynamo.discoveryBackend" . -}}
{{- $_ := include "shenron-dynamo.discoveryEndpoint" . -}}
{{- $_ := include "shenron-dynamo.natsServer" . -}}
{{- $requestPlane := include "shenron-dynamo.requestPlane" . -}}
{{- $workers := int (include "shenron-dynamo.totalWorkerReplicas" .) -}}
{{- if and (eq $requestPlane "http") (gt $workers 0) -}}
{{- $_ := include "shenron-dynamo.httpHost" . -}}
{{- $_ := include "shenron-dynamo.httpPort" . -}}
{{- $svcType := include "shenron-dynamo.workerServiceType" . -}}
{{- $basePort := int (include "shenron-dynamo.workerServiceBasePort" .) -}}
{{- $_ := include "shenron-dynamo.workerServicePortName" . -}}
{{- $fqdn := include "shenron-dynamo.caddyFqdn" . -}}
{{- if eq $fqdn "" -}}
{{- fail "values.caddy.fqdn must not be empty when worker replicas are enabled" -}}
{{- end -}}
{{- $maxPort := sub (add $basePort $workers) 1 -}}
{{- if and (eq $svcType "NodePort") (gt $maxPort 32767) -}}
{{- fail "workerService.basePort plus rendered replicas exceeds the NodePort range" -}}
{{- end -}}
{{- if gt $maxPort 65535 -}}
{{- fail "workerService.basePort plus rendered replicas exceeds port 65535" -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Return a worker's zero-based global ordinal across all enabled models.
*/}}
{{- define "shenron-dynamo.workerOrdinal" -}}
{{- $root := index . "root" -}}
{{- $targetModel := index . "modelName" -}}
{{- $targetReplica := int (index . "replica") -}}
{{- $ordinal := 0 -}}
{{- range $modelName := keys $root.Values.models | sortAlpha }}
{{- $model := index $root.Values.models $modelName -}}
{{- $replicas := int (get $model "replicas") -}}
{{- if eq $modelName $targetModel -}}
{{- $ordinal = add $ordinal $targetReplica -}}
{{- break -}}
{{- end -}}
{{- $ordinal = add $ordinal $replicas -}}
{{- end -}}
{{- $ordinal -}}
{{- end -}}

{{/*
Return a worker's unique external port.
*/}}
{{- define "shenron-dynamo.workerPort" -}}
{{- $root := index . "root" -}}
{{- $basePort := int (include "shenron-dynamo.workerServiceBasePort" $root) -}}
{{- $ordinal := int (include "shenron-dynamo.workerOrdinal" .) -}}
{{- add $basePort $ordinal -}}
{{- end -}}

{{/*
Generate the Caddyfile content for the ConfigMap.
*/}}
{{- define "shenron-dynamo.caddyfile" -}}
{{- $fqdn := include "shenron-dynamo.caddyFqdn" . -}}
{{- $host := ternary $fqdn ":80" (ne $fqdn "") }}
{{ $host }} {
{{- if eq (include "shenron-dynamo.replicaManagerEnabled" .) "true" }}
    handle_path /replica/* {
        reverse_proxy {{ include "shenron-dynamo.replicaManagerName" . }}:{{ include "shenron-dynamo.replicaManagerServicePort" . }}
    }
{{- end }}
{{- range .Values.caddy.extraRoutes }}
    handle {{ .path }} {
        reverse_proxy {{ .service }}:{{ .port }}
    }
{{- end }}
}
{{- end -}}
