{{/*
Find all TLS port numbers that we use.

If TLS is enabled and no explicit ".tls" value is given for port definition,
assume that TLS should be enabled. If explicitly turned off, honor that.

Outputs a comma-separated string of port numbers.
*/}}
{{- define "service-ssl-ports" }}
{{- $ports := "" }}
{{- range $.Values.service.ports }}
{{- if or .tls (and (eq (hasKey . "tls") false) $.Values.service.tls.enabled) }}
{{- $ports = print .port "," $ports }}
{{- end }}
{{- end }}
{{- $ports | trimSuffix "," }}
{{- end }}

{{- define "websocket-ssl-ports" }}
  {{- include "gameserver-websocket-service-ports" $ }}
{{- end }}

{{- define "ssl-ports" }}
  {{- $ports := list }}
  {{- $ports = concat $ports (splitList "," (include "service-ssl-ports" $)) }}
  {{- $ports = concat $ports (splitList "," (include "websocket-ssl-ports" $)) }}
  {{- $ports | uniq | join "," | trimSuffix "," -}}
{{- end }}

{{/*
Lists all targeted game server ports in a comma-sperated list of ports.
*/}}
{{- define "gameserver-pod-ports" }}
{{- $ports := list }}
{{- range $service_ports := $.Values.service.ports }}
{{- $ports = append $ports (default 9339 ($service_ports.targetPort | int)) }}
{{- end }}
{{- $ports | uniq | join "," }}
{{- end }}

{{/* List all of the service-side game server ports */}}
{{- define "gameserver-service-ports" }}
{{- $ports := list }}
{{- range $service_ports := $.Values.service.ports }}
{{- $ports = append $ports (default 9339 ($service_ports.port | int)) }}
{{- end }}
{{- $ports | uniq | join "," }}
{{- end }}

{{/* List all of the game server port names */}}
{{- define "gameserver-port-names" }}
{{- $ports := list }}
{{- range $service_ports := $.Values.service.ports }}
{{- $ports = append $ports (default "game" $service_ports.name) }}
{{- end }}
{{- $ports | uniq | join "," }}
{{- end }}

{{/* Service-side websocket port (fixed; not configurable). */}}
{{- define "gameserver-websocket-service-ports" -}}
9380
{{- end }}

{{/* Container-side websocket port (fixed; not configurable). */}}
{{- define "gameserver-websocket-pod-ports" -}}
9380
{{- end }}

{{/* Websocket port name (fixed; not configurable). */}}
{{- define "gameserver-websocket-port-names" -}}
websocket
{{- end }}

{{/*
Creates pod ports configuration section for exposing all server ports.
*/}}
{{- define "gameserver-port-configurations" }}
{{- $ports := dict -}}
{{- range $port := $.Values.service.ports -}}
{{- $containerPort := $port.targetPort | default 9339 -}}
{{- if eq (hasKey $ports ($containerPort | toString)) false -}}
- containerPort: {{ $containerPort }}
  name: {{ $port.name | default "game" }}
{{- $_ := set $ports ($containerPort | toString) $containerPort -}}
{{- end -}}
{{- end -}}
{{- end }}

{{/*
Creates a key-value dict from .Values.config.extraEnv list.
*/}}
{{- define "gameserver-extra-env-dict" }}
{{- $envs := dict -}}
{{- if and (hasKey $.Values.config "extraEnv") ($.Values.config.extraEnv) }}
{{- range $env := $.Values.config.extraEnv }}
{{ $_ := set $envs $env.name $env.value }}
{{- end }}
{{- end }}
{{ $envs | toJson }}
{{- end }}

{{/*
Helper function to check whether the infrastructure already advertises `Deployment.GrafanaUri` in
the `metaplay-deployment-runtime-options` Secret. Returns 1 if it does, otherwise 0.

Note that `lookup` yields nothing during `helm template` and `--dry-run` without cluster access, so
this reports 0 there and the chart falls back to deriving the URL itself.
*/}}
{{- define "metaplay.infraGrafanaUriExists" -}}
{{- $found := 0 -}}
{{- $secret := lookup "v1" "Secret" $.Release.Namespace "metaplay-deployment-runtime-options" -}}
{{- if $secret -}}
  {{- $encoded := dig "data" "options.json" "" $secret -}}
  {{- if $encoded -}}
    {{- $options := $encoded | b64dec | mustFromJson -}}
    {{- if dig "Deployment" "GrafanaUri" "" $options -}}
      {{- $found = 1 -}}
    {{- end -}}
  {{- end -}}
{{- end -}}
{{- $found -}}
{{- end }}

{{/*
Determine a Grafana URL to use with the following logic:

1. Prefer environment variable `Metaplay_Deployment__GrafanaUri`, if exists.
2. Emit nothing if the infrastructure already advertises `Deployment.GrafanaUri`, and let that value
   through instead. It is merged into the game server's runtime options after this chart's options
   so it would take precedence regardless, and staying silent keeps the chart from advertising a
   Grafana instance it does not own.
3. Otherwise construct based on best understanding a Grafana URL if Grafana is enabled.
*/}}
{{- define "grafana-url" }}
{{- $envs := include "gameserver-extra-env-dict" $ | fromJson }}
{{- if hasKey $envs "Metaplay_Deployment__GrafanaUri" -}}
{{ $envs.Metaplay_Deployment__GrafanaUri }}
{{- else if eq (include "metaplay.infraGrafanaUriExists" $) "1" -}}
{{- else if and ($.Values.grafana.enabled) ($.Values.admin.enabled) -}}
{{ include "gameserver-admin-url" $ }}grafana/
{{- end }}
{{- end }}

{{/*
Fetch grafana subchart version.
*/}}
{{- define "grafana-chart-version" }}
{{ (index .Subcharts "grafana").Chart.Version }}
{{- end }}

{{- define "gameserver-repo" }}
  {{- if eq $.Values.tenant.discoveryEnabled true }}
    {{- if and (hasKey $.Values.image "repository") $.Values.image.repository }}
      {{- $.Values.image.repository -}}
    {{- else }}
      {{- $deployment := include "discover-deployment" $ | mustFromJson }}
      {{- if not $deployment.ecr_repo }}
        {{- fail "could not find deployment `ecr_repo`!" }}
      {{- end }}
      {{- $deployment.ecr_repo -}}
    {{- end }}
  {{- else }}
    {{- $.Values.image.repository | default "" -}}
  {{- end }}
{{- end }}

{{- define "loki-endpoint-config" -}}
  {{- if $.Values.tenant.discoveryEnabled -}}
    {{- $config := (include "discover-observability" $ | mustFromJson) -}}
    {{- dict "endpoint" $config.loki_endpoint "username" $config.loki_username "password" $config.loki_password | mustToJson -}}
  {{- else if $.Values.experimental.infraLoki.enabled -}}
    {{- $config := (lookup "v1" "Secret" $.Release.Namespace $.Values.experimental.infraLoki.secretName) -}}
    {{- if hasKey $config "data" -}}
      {{- dict "endpoint" ($config.data.endpoint | b64dec) "username" ($config.data.username | b64dec) "password" ($config.data.password | b64dec) | mustToJson -}}
    {{- else -}}
      {{ fail ".Values.experimental.infraLoki.enabled was set to true but could not find {{ $.Values.experimental.infraLoki.secretName }} secret from {{ $.Release.Namespace }} namespace to discover Loki proxy details" }}
    {{- end -}}
  {{- else -}}
    {{- dict | mustToJson -}}
  {{- end -}}
{{- end -}}

{{- define "prometheus-endpoint-config" -}}
  {{- if $.Values.tenant.discoveryEnabled -}}
    {{- $config := (include "discover-observability" $ | mustFromJson) -}}
    {{- dict "endpoint" $config.prometheus_endpoint "username" $config.prometheus_username "password" $config.prometheus_password | mustToJson -}}
  {{- else if $.Values.experimental.infraPrometheus.enabled -}}
    {{- $config := (lookup "v1" "Secret" $.Release.Namespace $.Values.experimental.infraPrometheus.secretName) -}}
    {{- if hasKey $config "data" -}}
      {{- dict "endpoint" ($config.data.endpoint | b64dec) "username" ($config.data.username | b64dec) "password" ($config.data.password | b64dec) | mustToJson -}}
    {{- else -}}
      {{ fail ".Values.experimental.infraPrometheus.enabled was set to true but could not find {{ $.Values.experimental.infraPrometheus.secretName }} secret from {{ $.Release.Namespace }} namespace to discover Prometheus proxy details" }}
    {{- end -}}
  {{- else -}}
    {{- dict | mustToJson -}}
  {{- end }}
{{- end }}

{{/*
Determine the hostname of the game server (excluding domain part).
*/}}
{{- define "gameserver-hostname" }}
{{- if eq $.Values.tenant.discoveryEnabled true }}
  {{- $deployment := include "discover-deployment" $ | mustFromJson }}
  {{- if not $deployment.server_hostname }}
    {{- fail "could not discover server_hostname from kubernetes secret!" }}
  {{- end }}
  {{- $parts := splitn "." 2 $deployment.server_hostname }}
  {{- $parts._0 -}}
{{- else if $.Values.service.hostname }}
{{- $parts := splitn "." 2 $.Values.service.hostname }}
{{- $parts._0 -}}
{{- else if $.Values.hostname }}
{{- $parts := splitn "." 2 $.Values.hostname }}
{{- $parts._0 -}}
{{- else }}
{{ fail "Must provide either .Values.hostname or .Values.service.hostname" }}"
{{- end }}
{{- end }}

{{/*
Determine the domain of the game server (exclude hostname part).
*/}}
{{- define "gameserver-domain" }}
{{- if eq $.Values.tenant.discoveryEnabled true }}
  {{- $deployment := include "discover-deployment" $ | mustFromJson }}
  {{- if not $deployment.server_hostname }}
    {{- fail "could not discover server_hostname from kubernetes secret!" }}
  {{- end }}
  {{- $parts := splitn "." 2 $deployment.server_hostname }}
  {{- $parts._1 -}}
{{- else if $.Values.service.hostname }}
{{- $parts := splitn "." 2 $.Values.service.hostname }}
{{- $parts._1 -}}
{{- else if $.Values.hostname }}
{{- $parts := splitn "." 2 $.Values.hostname }}
{{- $parts._1 -}}
{{- end }}
{{- end }}

{{/*
Game server's FQDN (hostname + domain).
*/}}
{{- define "gameserver-fqdn" }}
{{- $.Values.service.hostname | default (printf "%s.%s" ( include "gameserver-hostname" $ ) ( include "gameserver-domain" $ )) -}}
{{- end }}

{{/*
Game server's admin/LiveOps dashboard's FQDN.
*/}}
{{- define "gameserver-admin-fqdn" }}
{{- $.Values.admin.hostname | default (printf "%s-admin.%s" ( include "gameserver-hostname" $ ) ( include "gameserver-domain" $ )) -}}
{{- end }}

{{/*
Game server's address for PublicWebApi.
*/}}
{{- define "gameserver-public-web-api-fqdn" }}
{{- $.Values.publicWebApi.hostname | default (printf "%s-public.%s" ( include "gameserver-hostname" $ ) ( include "gameserver-domain" $ )) -}}
{{- end }}

{{/*
Game server's CDN's FQDN.
*/}}
{{- define "gameserver-cdn-fqdn" }}
{{- $.Values.admin.hostname | default (printf "%s-assets.%s" ( include "gameserver-hostname" $ ) ( include "gameserver-domain" $ )) -}}
{{- end }}

{{/*
Game server's admin/LiveOps dashboard's URL (including protocol).
*/}}
{{- define "gameserver-admin-url" }}
{{- ternary "https://" "http://" $.Values.admin.tls.enabled }}{{ include "gameserver-admin-fqdn" $ }}/
{{- end }}

{{/*
Game server's admin/LiveOps dashboard's FQDN.
*/}}
{{- define "gameserver-api-fqdn" }}
{{- $.Values.api.hostname | default (printf "%s-api.%s" ( include "gameserver-hostname" $ ) ( include "gameserver-domain" $ )) -}}
{{- end }}

{{/*
Filter all YAML files from .Values.config.files and return them as a semicolon separated string.
*/}}
{{- define "config-files-yaml" }}
{{- $files := list }}
{{- range $file := $.Values.config.files }}
{{- if hasSuffix ".yaml" $file }}
{{- $files = append $files $file }}
{{- end }}
{{- end }}
{{- $files | uniq | join ";" }}
{{- end }}

{{/*
Filter all JSON files from .Values.config.files and return them as a semicolon separated string.
*/}}
{{- define "config-files-json" }}
{{- $files := list }}
{{- range $file := $.Values.config.files }}
{{- if hasSuffix ".json" $file }}
{{- $files = append $files $file }}
{{- end }}
{{- end }}
{{- $files | uniq | join ";" }}
{{- end }}

{{/*
Standard labels.
*/}}
{{- define "standard-labels" }}
"helm.sh/chart": "{{ $.Chart.Name }}-{{ $.Chart.Version | replace "+" "_" }}"
"app.kubernetes.io/managed-by": {{ $.Release.Service | quote }}
"app.kubernetes.io/instance": {{ $.Release.Name | quote }}
{{- end }}

{{/*
AWS ACM certificate for admin endpoints.
*/}}
{{- define "admin-ssl-cert-arn" }}
{{- if $.Values.admin.tls.sslCertArn }}
{{- $.Values.admin.tls.sslCertArn }}
{{- else }}
{{- $secret := (lookup "v1" "Secret" $.Release.Namespace "metaplay-config") }}
{{- if $secret.data.tls_arn }}
{{- $secret.data.tls_arn | b64dec }}
{{- else }}
{{ fail ".Values.admin.tls.sslCertArn was not found and could not find metaplay-config Secret from namespace" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
AWS ACM certificate for game endpoints.
*/}}
{{- define "service-ssl-cert-arn" }}
  {{- if eq $.Values.tenant.discoveryEnabled true }}
    {{- $deployment := include "discover-deployment" $ | mustFromJson }}
    {{- if not $deployment.server_tls_cert }}
      {{- fail "could not discover server_tls_cert from kubernetes secret!" }}
    {{- end }}
    {{- $deployment.server_tls_cert -}}
  {{- else if $.Values.service.tls.sslCertArn }}
    {{- $.Values.service.tls.sslCertArn }}
  {{- else }}
    {{- $secret := (lookup "v1" "Secret" $.Release.Namespace "metaplay-config") }}
    {{- if hasKey $secret "data" }}
      {{- if hasKey $secret.data "tls_arn" }}
        {{- $secret.data.tls_arn | b64dec }}
      {{- end }}
    {{- else }}
      {{ fail ".Values.service.tls.sslCertArn was not found and could not find metaplay-config Secret from namespace" }}
    {{- end }}
  {{- end }}
{{- end }}

{{/*
Obtain a value with a given key from `metaplay-config` Secret in $.Release.Namespace.

Usage: {{ include "metaplay-config-value" (list $ "config-value-name") }}
*/}}
{{- define "metaplay-config-value" }}
{{- $ctx := index . 0 }}
{{- $name := index . 1 }}
{{- $secret := (lookup "v1" "Secret" $ctx.Release.Namespace "metaplay-config") }}
{{- if hasKey $secret "data" }}
{{- if hasKey $secret.data $name }}
{{- get $secret.data $name | b64dec }}
{{- end }}
{{- end }}
{{- end }}

{{- define "auth-client-id" }}
{{ include "metaplay-config-value" (list . "auth_client_id") }}
{{- end }}

{{- define "auth-client-secret" }}
{{ include "metaplay-config-value" (list . "auth_client_secret") }}
{{- end }}

{{- define "resolve-rbac-role-name" }}
{{- $.Values.rbac.role.name | default "gameserver" }}
{{- end }}

{{- define "resolve-rbac-sa-name" }}
{{- if eq $.Values.rbac.serviceAccount.create true }}
{{- $.Values.rbac.serviceAccount.name | default "gameserver" }}
{{- else }}
{{- $.Values.rbac.serviceAccount.name | required "you must provide a non-empty name for the ServiceAccount when set rbac.serviceAccount.create = false" }}
{{- end }}
{{- end }}

{{/*
Usage: {{ include "add-prefixes-to-roles" (list "prefix" (list "role1" "role2")) }}

Outputs a comma separated list of prefixed roles (prefix given in as index 0, list of roles as index 1).

Can be easily consumed later, e.g. {{ include "add-prefixes-to-roles" (list ... ...) | splitList "," | toJson }}
*/}}
{{- define "add-prefixes-to-roles" }}
{{- $roles := list }}
{{- $prefix := index . 0 }}
{{- range $role := index . 1 }}
{{- $roles = append $roles (printf "%s%s" $prefix $role) }}
{{- end }}
{{- $roles | join "," }}
{{- end }}

{{- define "discover-observability" -}}
  {{- $jsonData := "{}" -}}
  {{- if $.Values.tenant.discoveryEnabled -}}
    {{- $secret := lookup "v1" "Secret" $.Release.Namespace $.Values.tenant.secretName -}}
    {{- if not (empty (dig "data" "observability" $secret)) -}}
      {{- $jsonData = $secret.data.observability | b64dec -}}
    {{- end -}}
  {{- end -}}
  {{- $jsonData -}}
{{- end -}}

{{- define "discover-deployment" -}}
  {{- $jsonData := "{}" -}}
  {{- if $.Values.tenant.discoveryEnabled -}}
    {{- $secret := lookup "v1" "Secret" $.Release.Namespace $.Values.tenant.secretName -}}
    {{- if not (empty (dig "data" "deployment" $secret)) -}}
      {{- $jsonData = $secret.data.deployment | b64dec -}}
    {{- end -}}
  {{- end -}}
  {{- $jsonData -}}
{{- end -}}

{{/* Validation tests */}}
{{- define "test-port-collisions" }}
{{- $gameserver_service_ports := splitList "," (include "gameserver-service-ports" $) }}
{{- $websocket_service_ports := splitList "," (include "gameserver-websocket-service-ports" $) }}
{{- range $websocket_port := $websocket_service_ports }}
{{- if has $websocket_port $gameserver_service_ports }}
{{ fail (cat "Websocket service port" $websocket_port "is also defined as a game server client port! Ports must be unique to either game server client ports or websocket ports!") }}
{{- end }}
{{- end }}
{{- $gameserver_pod_ports := splitList "," (include "gameserver-pod-ports" $) }}
{{- $websocket_pod_ports := splitList "," (include "gameserver-websocket-pod-ports" $) }}
{{- range $websocket_port := $websocket_pod_ports }}
{{- if has $websocket_port $gameserver_pod_ports }}
{{ fail (cat "Websocket pod port" $websocket_port "is also defined as a game server pod port! Ports must be unique to either game server client ports or websocket ports!") }}
{{- end }}
{{- end }}
{{- $gameserver_port_names := splitList "," (include "gameserver-port-names" $) }}
{{- $websocket_port_names := splitList "," (include "gameserver-websocket-port-names" $) }}
{{- range $websocket_name := $websocket_port_names }}
{{- if has $websocket_name $gameserver_port_names }}
{{ fail (cat "Websocket port name" $websocket_name "is also defined as a game server port name! Port names be unique to either game server client port names or websocket port names!") }}
{{- end }}
{{- end }}
{{- end }}

{{- define "test-grafana" -}}
  {{- if (and $.Values.grafana.enabled $.Values.experimental.grafanaOperator.enabled) -}}
    {{ fail "Both .grafana.enabled and .experimental.grafanaOperator.enabled cannot be turned on at the same time." }}
  {{- end -}}
{{- end -}}

{{- define "test-deprecated-gameserver-crd" }}
  {{/*
    Looking up an API that the cluster has never registered behaves
    differently between helm subcommands:
      - `helm template` returns nil — the .items check below would
        then panic with "len of nil pointer".
      - `helm install` / `helm upgrade` propagate the API discovery
        error as a hard template failure: "error calling lookup: unable
        to get apiresource from unstructured: metaplay.io/v1, Kind=
        GameServer: the server could not find the requested resource".

    Gate the lookup on the legacy API actually being registered, so the
    check self-disables both on fresh clusters that never had the CRD
    and on newer stacks where it has since been removed. When the API is
    absent there can be no legacy resources to find, so skipping loses
    nothing. `.Values.deprecatedCrdCheck.enabled` remains as an explicit
    opt-out.

    Note that .Capabilities.APIVersions is populated from cluster
    discovery for `helm install` / `helm upgrade` — the paths that
    produced the hard failure above — but not for `helm template`, not
    even with `--dry-run=server` (which enables `lookup` yet leaves
    capabilities at their built-in defaults). The check is therefore
    inert when rendering templates, which is also where `lookup` would
    have returned nil; pass `--api-versions metaplay.io/v1/GameServer`
    to exercise it there.
  */}}
  {{- if .Values.deprecatedCrdCheck | default dict | dig "enabled" true }}
    {{- if $.Capabilities.APIVersions.Has "metaplay.io/v1/GameServer" }}
      {{- $gameservers := lookup "metaplay.io/v1" "GameServer" $.Release.Namespace "" }}
      {{- if and $gameservers (gt (len $gameservers.items) 0) }}
        {{- fail (cat "You have legacy metaplay.io/v1 GameServer resources deployed in namespace" $.Release.Namespace ". Please uninstall earlier game deployments and retry!" )}}
      {{- end }}
    {{- end }}
  {{- end }}
{{- end }}

{{/* Check that the game server image configs are not empty */}}
{{- define "test-game-server-image" -}}
  {{- $repository := (include "gameserver-repo" $) }}
  {{- if or (not $repository) (empty $repository) }}
    {{- fail "Could not determine game server image repository: you must either set it with .Values.image.repository or by setting .Values.tenant.discoveryEnabled to true." }}
  {{- end }}
  {{- if or (not $.Values.image.tag) (empty $.Values.image.tag) }}
    {{- fail "Game server image tag was not defined; you must set it via the .Values.image.tag value." }}
  {{- end }}
{{- end }}

{{/*
Helper function to check if the ConfigMap 'metaplay-infra-migration' exists in the current namespace using Helm's lookup function.
Returns 1 if the ConfigMap exists, otherwise 0.
*/}}
{{- define "metaplay.infraMigrationConfigMapExists" -}}
{{- $cm := lookup "v1" "ConfigMap" .Release.Namespace "metaplay-infra-migration" -}}
{{- if $cm }}1{{ else }}0{{ end -}}
{{- end }}

{{/*
Helper function to determine if infra migration is enabled.
Returns 1 if the ConfigMap exists, otherwise falls back to .Values.infraMigration (1 or 0).
*/}}
{{- define "metaplay.infraMigrationEnabled" -}}
{{- if eq (include "metaplay.infraMigrationConfigMapExists" .) "1" -}}
1
{{- else -}}
{{- if .Values.infraMigration }}1{{ else }}0{{ end -}}
{{- end -}}
{{- end }}
