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

{{/*
Create a fully qualified app name.
*/}}
{{- define "shenron.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := include "shenron.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.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Common labels.
*/}}
{{- define "shenron.labels" -}}
helm.sh/chart: {{ include "shenron.chart" . }}
app.kubernetes.io/name: {{ include "shenron.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.selectorLabels" -}}
app.kubernetes.io/name: {{ include "shenron.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}

{{/*
Sanitize an arbitrary model key into a DNS-safe slug.
*/}}
{{- define "shenron.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.modelHash" -}}
{{- sha256sum (printf "%v" .) | trunc 8 -}}
{{- end -}}

{{/*
Stable model id for labels.
*/}}
{{- define "shenron.modelID" -}}
{{- $slug := include "shenron.modelSlug" . -}}
{{- $hash := include "shenron.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.modelResourceName" -}}
{{- $root := index . "root" -}}
{{- $modelName := index . "modelName" -}}
{{- $fullname := include "shenron.fullname" $root -}}
{{- $modelID := include "shenron.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 for onwards Deployment/Service.
*/}}
{{- define "shenron.onwardsName" -}}
{{- printf "%s-onwards" (include "shenron.fullname" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Resource name for onwards config ConfigMap.
*/}}
{{- define "shenron.onwardsConfigName" -}}
{{- printf "%s-config" (include "shenron.onwardsName" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}

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

{{/*
Resolve and validate the shared model port.
*/}}
{{- define "shenron.sharedPort" -}}
{{- if not (hasKey .Values "port") -}}
{{- fail "values.port is required and is shared by all models" -}}
{{- end -}}
{{- $port := int .Values.port -}}
{{- if or (lt $port 1) (gt $port 65535) -}}
{{- fail "values.port must be between 1 and 65535" -}}
{{- end -}}
{{- $port -}}
{{- end -}}

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

{{/*
Resolve and validate onwards listen port.
*/}}
{{- define "shenron.onwardsPort" -}}
{{- $onwards := .Values.onwards | default dict -}}
{{- $port := 3000 -}}
{{- if hasKey $onwards "port" -}}
{{- $port = int (get $onwards "port") -}}
{{- end -}}
{{- if or (lt $port 1) (gt $port 65535) -}}
{{- fail "values.onwards.port must be between 1 and 65535" -}}
{{- end -}}
{{- $port -}}
{{- end -}}

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

{{/*
Resolve and validate onwards service nodePort.
*/}}
{{- define "shenron.onwardsServiceNodePort" -}}
{{- $onwards := .Values.onwards | default dict -}}
{{- $service := get $onwards "service" | default dict -}}
{{- $nodePort := 0 -}}
{{- if hasKey $service "nodePort" -}}
{{- $nodePort = int (get $service "nodePort") -}}
{{- end -}}
{{- if ne $nodePort 0 -}}
{{- if or (lt $nodePort 30000) (gt $nodePort 32767) -}}
{{- fail "values.onwards.service.nodePort must be between 30000 and 32767" -}}
{{- end -}}
{{- end -}}
{{- $nodePort -}}
{{- end -}}

{{/*
Resolve and validate onwards API key secret name.
*/}}
{{- define "shenron.onwardsApiSecretName" -}}
{{- $onwards := .Values.onwards | default dict -}}
{{- $secret := get $onwards "systemApiKeySecret" | default dict -}}
{{- $name := "system-api-key" -}}
{{- if hasKey $secret "name" -}}
{{- $name = printf "%v" (get $secret "name") -}}
{{- end -}}
{{- if eq (trim $name) "" -}}
{{- fail "values.onwards.systemApiKeySecret.name must not be empty" -}}
{{- end -}}
{{- if not (regexMatch "^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$" $name) -}}
{{- fail "values.onwards.systemApiKeySecret.name must be a lowercase RFC1123 subdomain (e.g. system-api-key)" -}}
{{- end -}}
{{- $name -}}
{{- end -}}

{{/*
Resolve and validate onwards API key secret key.
*/}}
{{- define "shenron.onwardsApiSecretKey" -}}
{{- $onwards := .Values.onwards | default dict -}}
{{- $secret := get $onwards "systemApiKeySecret" | default dict -}}
{{- $key := "SYSTEM_API_KEY" -}}
{{- if hasKey $secret "key" -}}
{{- $key = printf "%v" (get $secret "key") -}}
{{- end -}}
{{- if eq (trim $key) "" -}}
{{- fail "values.onwards.systemApiKeySecret.key must not be empty" -}}
{{- end -}}
{{- $key -}}
{{- end -}}

{{/*
Validate onwards settings.
*/}}
{{- define "shenron.validateOnwards" -}}
{{- $_ := include "shenron.onwardsEnabled" . -}}
{{- $_ := include "shenron.onwardsPort" . -}}
{{- $_ := include "shenron.onwardsServicePort" . -}}
{{- $_ := include "shenron.onwardsServiceNodePort" . -}}
{{- $_ := include "shenron.onwardsApiSecretName" . -}}
{{- $_ := include "shenron.onwardsApiSecretKey" . -}}
{{- end -}}

{{/*
Resolve and validate Hugging Face cache enabled flag.
*/}}
{{- define "shenron.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.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.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.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.validateHuggingfaceCache" -}}
{{- $_ := include "shenron.hfCacheEnabled" . -}}
{{- $_ := include "shenron.hfCacheHostPath" . -}}
{{- $_ := include "shenron.hfCacheMountPath" . -}}
{{- $_ := include "shenron.hfCacheHostPathType" . -}}
{{- end -}}

{{/*
Validate per-model required fields and types.
*/}}
{{- define "shenron.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; use top-level values.port" $modelName) -}}
{{- 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 -}}
{{- if eq (printf "%v" (get $item "name")) "" -}}
{{- fail (printf "models.%s.env[%d].name must not be empty" $modelName $idx) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if hasKey $model "image" -}}
{{- $image := get $model "image" -}}
{{- if not (kindIs "map" $image) -}}
{{- fail (printf "models.%s.image 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 $resources "limits" -}}
{{- $limits := get $resources "limits" -}}
{{- if not (kindIs "map" $limits) -}}
{{- fail (printf "models.%s.resources.limits must be an object" $modelName) -}}
{{- end -}}
{{- end -}}
{{- if hasKey $resources "requests" -}}
{{- $requests := get $resources "requests" -}}
{{- if not (kindIs "map" $requests) -}}
{{- fail (printf "models.%s.resources.requests must be an object" $modelName) -}}
{{- end -}}
{{- 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 -}}
