{{- /*
Deployment skip-render gate.

Pre-#943 the Deployment skipped silently unless the operator manually
provided three Secrets (database DSN, app credentials, optional OIDC
client secret) via per-Sovereign overlay. On a freshly franchised
Sovereign nothing populated those values — the chart emitted Service +
ConfigMap + NetworkPolicy but no Pod, so NewAPI never came up and
alice signup gate 5 (LLM) failed.

Issue #943 fix: chart auto-provisions BOTH the CNPG-backed Postgres
DSN Secret (templates/cnpg-cluster.yaml, gated on .Values.cnpg.enabled)
AND the SESSION_SECRET/CRYPTO_SECRET credentials Secret (templates/
credentials-secret.yaml, gated on .Values.credentials.autoProvision).
Defaults flip both to true so a Sovereign install with bp-newapi at
slot 80 lands a real Pod without operator intervention.

Effective DSN Secret name resolution:
  1. .Values.database.existingSecret (operator override) — if set, use
     it directly (the contabo path or a per-Sovereign external Postgres).
  2. else when .Values.cnpg.enabled the chart auto-emits its own DSN
     Secret named `<release>-newapi-db-dsn`.
  3. else the Deployment skip-renders (legacy path — preserved for
     environments where neither auto-provision nor a manual Secret is
     wired).

Same precedence for credentials: explicit existingSecret > auto-emitted
`<release>-app-creds` > skip-render.

Keycloak OIDC client secret is required only when
`auth.adminUI.mode=keycloak` AND `auth.adminUI.keycloak.issuer` is set;
the configmap mirrors this gate.
*/ -}}
{{- $kcMode := eq .Values.auth.adminUI.mode "keycloak" -}}
{{- $kcConfigured := and $kcMode .Values.auth.adminUI.keycloak.issuer .Values.auth.adminUI.keycloak.existingSecret -}}
{{- /* Resolve database secret: explicit override > chart-emitted CNPG DSN */ -}}
{{- $effDbSecret := .Values.database.existingSecret -}}
{{- $effDbKey := .Values.database.existingSecretKey | default "SQL_DSN" -}}
{{- if and (not $effDbSecret) .Values.cnpg.enabled -}}
{{- $effDbSecret = default (printf "%s-newapi-db-dsn" (include "bp-newapi.fullname" .)) .Values.cnpg.dsnSecretName -}}
{{- end -}}
{{- /* Resolve credentials secret: explicit override > chart auto-provision */ -}}
{{- $effCredsSecret := .Values.credentials.existingSecret -}}
{{- if and (not $effCredsSecret) .Values.credentials.autoProvision -}}
{{- $effCredsSecret = default (printf "%s-app-creds" (include "bp-newapi.fullname" .)) .Values.credentials.autoSecretName -}}
{{- end -}}
{{- $deployReady := and .Values.newapi.enabled $effDbSecret $effCredsSecret -}}
{{- if and $kcMode (not $kcConfigured) -}}
{{- $deployReady = false -}}
{{- end -}}
{{- if $deployReady }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "bp-newapi.fullname" . }}
  labels:
    {{- include "bp-newapi.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.newapi.replicas }}
  selector:
    matchLabels:
      {{- include "bp-newapi.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "bp-newapi.selectorLabels" . | nindent 8 }}
      annotations:
        # Roll the pod when channel/policy config changes.
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
    spec:
      serviceAccountName: {{ include "bp-newapi.serviceAccountName" . }}
      {{- with .Values.imagePullSecrets }}
      # Issue #952: NewAPI image (`newapi-mirror`) and the metering
      # sidecar both live in PRIVATE ghcr.io/openova-io/openova/*
      # repositories — kubelet must authenticate to pull. The canonical
      # `ghcr-pull` Secret is reflected into every namespace listed in
      # bp-reflector's annotations on flux-system/ghcr-pull (cloud-init
      # writes the source Secret with `reflection-auto-namespaces`
      # explicitly enumerating `newapi`). Without these references the
      # Pod stalls in ImagePullBackOff with a 403 Forbidden, blocking
      # alice signup gate 5 (LLM) on every fresh Sovereign.
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      securityContext:
        {{- toYaml .Values.newapi.securityContext | nindent 8 }}
      containers:
        - name: newapi
          image: "{{ .Values.newapi.image.repository }}:{{ .Values.newapi.image.tag }}"
          imagePullPolicy: {{ .Values.newapi.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.newapi.port }}
              protocol: TCP
          env:
            # ── Database (Postgres on bp-cnpg) ────────────────────────
            # Secret name resolution (issue #943):
            #   1. .Values.database.existingSecret if set (operator override)
            #   2. else chart-emitted DSN Secret from cnpg-cluster.yaml
            #      (`<release>-newapi-db-dsn`) when .Values.cnpg.enabled
            - name: SQL_DSN
              valueFrom:
                secretKeyRef:
                  name: {{ $effDbSecret }}
                  key: {{ $effDbKey }}

            # ── Valkey cache ──────────────────────────────────────────
            {{- if .Values.valkey.enabled }}
            {{- if .Values.valkey.existingSecret }}
            - name: REDIS_CONN_STRING
              valueFrom:
                secretKeyRef:
                  name: {{ .Values.valkey.existingSecret }}
                  key: {{ .Values.valkey.existingSecretKey }}
            {{- else }}
            - name: REDIS_CONN_STRING
              value: {{ .Values.valkey.url | quote }}
            {{- end }}
            {{- end }}

            # ── App credentials (NewAPI-internal) ─────────────────────
            # Secret name resolution (issue #943):
            #   1. .Values.credentials.existingSecret (operator override)
            #   2. else chart-emitted credentials-secret.yaml output
            #      when .Values.credentials.autoProvision (DEFAULT true)
            - name: SESSION_SECRET
              valueFrom:
                secretKeyRef:
                  name: {{ $effCredsSecret }}
                  key: SESSION_SECRET
            - name: CRYPTO_SECRET
              valueFrom:
                secretKeyRef:
                  name: {{ $effCredsSecret }}
                  key: CRYPTO_SECRET

            # ── OIDC (admin UI) ───────────────────────────────────────
            {{- if $kcConfigured }}
            - name: OIDC_CLIENT_SECRET
              valueFrom:
                secretKeyRef:
                  name: {{ .Values.auth.adminUI.keycloak.existingSecret }}
                  key: OIDC_CLIENT_SECRET
            {{- end }}

            # ── Bootstrap-only master key (DEV ONLY) ──────────────────
            {{- if .Values.auth.adminUI.masterKeySecret }}
            - name: MASTER_KEY
              valueFrom:
                secretKeyRef:
                  name: {{ .Values.auth.adminUI.masterKeySecret }}
                  key: MASTER_KEY
            {{- end }}

          envFrom:
            - configMapRef:
                name: {{ include "bp-newapi.configMapName" . }}

          volumeMounts:
            - name: channels
              mountPath: /etc/newapi
              readOnly: true

          livenessProbe:
            httpGet:
              path: {{ .Values.newapi.probes.liveness.path }}
              port: {{ .Values.newapi.probes.liveness.port }}
            initialDelaySeconds: {{ .Values.newapi.probes.liveness.initialDelaySeconds }}
            periodSeconds: {{ .Values.newapi.probes.liveness.periodSeconds }}
            timeoutSeconds: {{ .Values.newapi.probes.liveness.timeoutSeconds }}
            failureThreshold: {{ .Values.newapi.probes.liveness.failureThreshold }}
          readinessProbe:
            httpGet:
              path: {{ .Values.newapi.probes.readiness.path }}
              port: {{ .Values.newapi.probes.readiness.port }}
            initialDelaySeconds: {{ .Values.newapi.probes.readiness.initialDelaySeconds }}
            periodSeconds: {{ .Values.newapi.probes.readiness.periodSeconds }}
            timeoutSeconds: {{ .Values.newapi.probes.readiness.timeoutSeconds }}
            failureThreshold: {{ .Values.newapi.probes.readiness.failureThreshold }}

          resources:
            {{- toYaml .Values.newapi.resources | nindent 12 }}

        {{- if .Values.meteringSidecar.enabled }}
        # ── Metering sidecar (#798) ─────────────────────────────
        # Transparent reverse proxy in front of NewAPI. Observes
        # successful /v1/* responses, emits one envelope per
        # completed LLM request on NATS subject
        # `catalyst.usage.recorded`. NewAPI itself is unmodified —
        # see values.yaml `meteringSidecar` for the architecture
        # rationale.
        - name: metering-sidecar
          image: "{{ .Values.meteringSidecar.image.repository }}:{{ .Values.meteringSidecar.image.tag }}"
          imagePullPolicy: {{ .Values.meteringSidecar.image.pullPolicy }}
          ports:
            - name: metering
              containerPort: {{ .Values.meteringSidecar.port }}
              protocol: TCP
          env:
            - name: LISTEN_PORT
              value: {{ .Values.meteringSidecar.port | quote }}
            # NewAPI forwards on the same pod's loopback. NewAPI's
            # `newapi.port` is the upstream container port — use
            # 127.0.0.1 (NOT the Service DNS) so the sidecar's
            # response observation does not loop through the
            # cluster-fronting Service.
            - name: NEWAPI_UPSTREAM_URL
              value: "http://127.0.0.1:{{ .Values.newapi.port }}"
            - name: NATS_URL
              value: {{ .Values.meteringSidecar.natsURL | default "nats://nats-jetstream.nats-system.svc.cluster.local:4222" | quote }}
            - name: NATS_PUBLISH_TIMEOUT
              value: {{ .Values.meteringSidecar.natsPublishTimeout | quote }}
            - name: PRICE_MICRO_OMR_PER_TOKEN
              value: {{ .Values.meteringSidecar.priceMicroOMRPerToken | quote }}
            - name: SPOOL_DIR
              value: {{ .Values.meteringSidecar.spoolDir | quote }}
            - name: CUSTOMER_ID_HEADER
              value: {{ .Values.meteringSidecar.customerIDHeader | quote }}
            - name: TENANT_ID_HEADER
              value: {{ .Values.meteringSidecar.tenantIDHeader | quote }}
          livenessProbe:
            httpGet:
              path: /healthz
              port: {{ .Values.meteringSidecar.port }}
            initialDelaySeconds: 5
            periodSeconds: 10
            timeoutSeconds: 3
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /healthz
              port: {{ .Values.meteringSidecar.port }}
            initialDelaySeconds: 2
            periodSeconds: 5
            timeoutSeconds: 3
            failureThreshold: 3
          resources:
            {{- toYaml .Values.meteringSidecar.resources | nindent 12 }}
          securityContext:
            {{- toYaml .Values.meteringSidecar.securityContext | nindent 12 }}
          volumeMounts:
            - name: metering-spool
              mountPath: {{ .Values.meteringSidecar.spoolDir }}
        {{- end }}

      volumes:
        - name: channels
          configMap:
            name: {{ include "bp-newapi.configMapName" . }}
            items:
              - key: channels.yaml
                path: channels.yaml
        {{- if .Values.meteringSidecar.enabled }}
        # emptyDir is intentional: the sidecar's drain loop
        # republishes spooled envelopes once NATS recovers, so
        # persistence across pod restarts is unnecessary.
        - name: metering-spool
          emptyDir:
            sizeLimit: 64Mi
        {{- end }}
{{- end }}
