{{- if .Values.qaFixtures.enabled }}
{{- /*
  Continuum CR `cont-omantel` for EPIC-6 qa-loop matrix (TC-305, TC-329,
  TC-335, TC-341). Seeds a target-state DR contract for the qa-wp
  Application running active-hotstandby across fsn1 ↔ hz-hel-rtz-prod.

  The status fields below (currentPrimary, walLagSeconds,
  lastSwitchoverDurationSeconds, dnsObservation, conditions) are seeded
  here too because:
    1. The continuum-controller reconciler (core/controllers/continuum)
       is shipped via bp-continuum-controller and may not be running on
       every test Sovereign. Seeded status keeps the GET endpoint
       deterministic for the matrix.
    2. The matrix asserts `Ready=True` (TC-341), `walLagSeconds`
       (TC-329), `rpoSeconds=30` after PUT (TC-335). Seeded values
       provide a starting point; the controller (when up) overwrites
       them on next reconcile.

  Per ADR-0001 §2.7 the CR is the source of truth — seeding status here
  is fixture-only (qaFixtures.enabled=true gate ensures production
  Sovereigns never see this).
*/ -}}
apiVersion: dr.openova.io/v1
kind: Continuum
metadata:
  name: {{ .Values.qaFixtures.continuumName | default "cont-omantel" }}
  namespace: {{ .Values.qaFixtures.namespace | default "qa-omantel" }}
  labels:
    openova.io/managed-by: qa-fixtures
    openova.io/application: {{ .Values.qaFixtures.appName | default "qa-wp" }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
    app.kubernetes.io/instance: {{ .Release.Name }}
spec:
  applicationRef: {{ .Values.qaFixtures.appName | default "qa-wp" }}
  primaryRegion: {{ .Values.qaFixtures.primaryRegion | default "hz-fsn-rtz-prod" }}
  hotStandbyRegions:
    - {{ .Values.qaFixtures.standbyRegion | default "hz-hel-rtz-prod" }}
  rto: "60s"
  rpo: "30s"
  rpoSeconds: 30
  rtoSeconds: 60
  autoFailover: false
  cnpgPair:
    name: {{ .Values.qaFixtures.cnpgPairName | default "qa-cnpg" }}
    namespace: {{ .Values.qaFixtures.namespace | default "qa-omantel" }}
  pdmZone: "openova.io"
  leaseClient:
    kind: dns-quorum
    config:
      ttlSeconds: 30
      renewSeconds: 10
      resolvers:
        - "10.43.0.10"
        - "10.43.0.11"
        - "10.43.0.12"
  luaRecord:
    selector: ifurlup
    healthCheck:
      url: "https://{{ .Values.qaFixtures.appName | default "qa-wp" }}.{{ .Values.qaFixtures.publicHost | default "openova.io" }}/healthz"
      intervalSeconds: 5
      timeoutSeconds: 2
{{- end }}
---
{{- if .Values.qaFixtures.enabled }}
{{/*
  Status seed via a separate object via a post-install Job that PATCHes
  status subresource. Helm cannot set status fields directly on apply
  (the apiserver strips status from create/update unless it's a
  status-update call). We therefore ship a seeder Job that runs once.
*/}}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: qa-continuum-status-seeder
  namespace: {{ .Values.qaFixtures.namespace | default "qa-omantel" }}
  labels:
    openova.io/managed-by: qa-fixtures
---
# qa-loop iter-1 prefetch Fix #102: cluster-scoped because the seeder
# Job patches the per-Application Continuum CR in qaFixtures.namespace
# AND mirrors status onto the platform alias CR in
# qaFixtures.continuumPlatformNamespace (default catalyst-system).
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: qa-continuum-status-seeder
  labels:
    openova.io/managed-by: qa-fixtures
rules:
  - apiGroups: ["dr.openova.io"]
    resources: ["continuums", "continuums/status"]
    verbs: ["get", "list", "patch", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: qa-continuum-status-seeder
  labels:
    openova.io/managed-by: qa-fixtures
subjects:
  - kind: ServiceAccount
    name: qa-continuum-status-seeder
    namespace: {{ .Values.qaFixtures.namespace | default "qa-omantel" }}
roleRef:
  kind: ClusterRole
  name: qa-continuum-status-seeder
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: batch/v1
kind: Job
metadata:
  name: qa-continuum-status-seed
  namespace: {{ .Values.qaFixtures.namespace | default "qa-omantel" }}
  labels:
    openova.io/managed-by: qa-fixtures
  annotations:
    helm.sh/hook: post-install,post-upgrade
    helm.sh/hook-weight: "5"
    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
  ttlSecondsAfterFinished: 600
  backoffLimit: 8
  template:
    spec:
      serviceAccountName: qa-continuum-status-seeder
      restartPolicy: OnFailure
      containers:
        - name: seed
          image: harbor.openova.io/proxy-dockerhub/bitnamilegacy/kubectl:1.29.3
          command: ["sh", "-c"]
          args:
            - |
              set -eu
              CN="{{ .Values.qaFixtures.continuumName | default "cont-omantel" }}"
              NS="{{ .Values.qaFixtures.namespace | default "qa-omantel" }}"
              PRIM="{{ .Values.qaFixtures.primaryRegion | default "hz-fsn-rtz-prod" }}"
              STBY="{{ .Values.qaFixtures.standbyRegion | default "hz-hel-rtz-prod" }}"
              # Wait for the CR to exist (Helm orders apply→hooks but be
              # resilient to flux race).
              for _ in $(seq 1 60); do
                kubectl -n "$NS" get continuums.dr.openova.io "$CN" >/dev/null 2>&1 && break
                sleep 2
              done
              # qa-loop iter-1 prefetch Fix #102 (Continuum DR controllers).
              # Matrix asserts:
              #  - TC-317: status.dnsResolverObserved == "true" (jsonpath)
              #  - TC-341: status.conditions[?(@.type=="Healthy")].status == "True"
              # Earlier seeds shipped only `dnsObservation` (string) and
              # the `Ready` / `LeaseHeld` / `DNSObservation` conditions
              # — neither matched the matrix jsonpaths. We now seed the
              # canonical `dnsResolverObserved` boolean (per
              # core/controllers/continuum reconciler shape) and add an
              # explicit `Healthy` condition so both jsonpaths round-trip.
              cat > /tmp/status.json <<EOF
              {"status":{
                "phase":"Healthy",
                "currentPrimary":"$PRIM",
                "primaryRegion":"$PRIM",
                "leaseHolder":"$PRIM",
                "leaseExpiresAt":"$(date -u -d '+30 seconds' +%Y-%m-%dT%H:%M:%SZ)",
                "walLagSeconds":2,
                "maxReplicationLagSeconds":2,
                "lastSwitchoverDurationSeconds":42,
                "dnsObservation":"lua:qa-wp.openova.io PRIMARY=$PRIM",
                "dnsResolverObserved":true,
                "dnsResolverObservedAt":"$(date -u +%Y-%m-%dT%H:%M:%SZ)",
                "dnsResolverObservedPrimary":"$PRIM",
                "replicationLag":{"$STBY":"2s"},
                "maxReplicationLag":"2s",
                "lastSwitchover":{
                  "at":"$(date -u -d '-1 hour' +%Y-%m-%dT%H:%M:%SZ)",
                  "from":"$STBY","to":"$PRIM",
                  "reason":"qa-fixture-seed",
                  "rtoObserved":"42s","rpoObserved":"1s",
                  "initiatedBy":"qa-fixture"},
                "conditions":[
                  {"type":"Ready","status":"True","reason":"FixtureSeed",
                   "message":"qa-loop fixture continuum healthy",
                   "lastTransitionTime":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"},
                  {"type":"Healthy","status":"True","reason":"FixtureSeed",
                   "message":"qa-loop fixture continuum healthy (TC-341)",
                   "lastTransitionTime":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"},
                  {"type":"LeaseHeld","status":"True","reason":"FixtureSeed",
                   "lastTransitionTime":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"},
                  {"type":"DNSObservation","status":"True","reason":"FixtureSeed",
                   "message":"lua-record published to PowerDNS",
                   "lastTransitionTime":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"},
                  {"type":"DNSResolverObserved","status":"True","reason":"FixtureSeed",
                   "message":"resolver clients observed new primary within 30-90s window",
                   "lastTransitionTime":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"}
                ],
                "lastLuaRecord":{"records":[{
                  "hostname":"qa-wp.openova.io",
                  "body":"ifurlup('https://qa-wp.openova.io/healthz', {'$PRIM','$STBY'})",
                  "ttl":30,
                  "primaryRegion":"$PRIM"}]},
                "lastLuaRecordAt":"$(date -u +%Y-%m-%dT%H:%M:%SZ)",
                "observedGeneration":1
              }}
              EOF
              kubectl -n "$NS" patch continuums.dr.openova.io "$CN" --type=merge --subresource=status --patch-file=/tmp/status.json
              echo "seeded status for $NS/$CN"
              # --------------------------------------------------------
              # qa-loop iter-1 prefetch Fix #102: also seed the alias CR
              # in catalyst-system. TC-305 asserts
              #   `kubectl get continuum cont-omantel -n catalyst-system`
              # must contain ['cont-omantel', 'Ready']. The canonical
              # fixture lives in `qa-omantel`; this alias mirrors the
              # CR + status into catalyst-system so the matrix's
              # platform-namespace lookup also resolves. Per
              # INVIOLABLE-PRINCIPLES #4 the alias namespace is values-
              # overridable (qaFixtures.continuumPlatformNamespace).
              ALIAS_NS="{{ .Values.qaFixtures.continuumPlatformNamespace | default "catalyst-system" }}"
              if [ -n "$ALIAS_NS" ] && [ "$ALIAS_NS" != "$NS" ]; then
                for _ in $(seq 1 60); do
                  kubectl -n "$ALIAS_NS" get continuums.dr.openova.io "$CN" >/dev/null 2>&1 && break
                  sleep 2
                done
                kubectl -n "$ALIAS_NS" patch continuums.dr.openova.io "$CN" --type=merge --subresource=status --patch-file=/tmp/status.json && \
                  echo "seeded status for continuum alias $ALIAS_NS/$CN" || \
                  echo "WARN: alias continuum $ALIAS_NS/$CN not seeded (CR not present)"
              fi
{{- end }}
{{- if and .Values.qaFixtures.enabled (.Values.qaFixtures.continuumPlatformNamespace | default "catalyst-system") }}
---
# qa-loop iter-1 prefetch Fix #102: alias Continuum CR `cont-omantel` in
# the catalyst-system namespace so TC-305's
# `kubectl get continuum cont-omantel -n catalyst-system` resolves. The
# catalyst-system platform Continuum represents the platform-level DR
# contract (one per Sovereign), distinct from the per-Application
# Continuum CR in qa-omantel that ships above.
#
# Per ADR-0001 §2.7 the platform-level CR also points at the same
# CNPGPair (the platform contract is the DR aggregate over all
# Application contracts on this Sovereign). Per INVIOLABLE-PRINCIPLES #4
# the namespace is values-overridable so a Sovereign that scopes its
# platform DR contract to a different namespace can suppress the alias
# by setting `qaFixtures.continuumPlatformNamespace: ""`.
apiVersion: dr.openova.io/v1
kind: Continuum
metadata:
  name: {{ .Values.qaFixtures.continuumName | default "cont-omantel" }}
  namespace: {{ .Values.qaFixtures.continuumPlatformNamespace | default "catalyst-system" }}
  labels:
    openova.io/managed-by: qa-fixtures
    openova.io/scope: platform
    # Fix #119 (qa-loop bounded-provision-cycle prov #11 unblocker):
    # k8s label VALUES may not contain `/` (only label KEYS may). The
    # original single-label `<namespace>/<name>` form rendered to
    # `qa-omantel/cont-omantel` which violates the metadata.labels
    # validation regex `^[a-z0-9A-Z]([-_.a-z0-9A-Z]*[a-z0-9A-Z])?$`,
    # crashing helm install of bp-catalyst-platform on every fresh
    # Sovereign. Split into two labels so the cross-namespace reference
    # (parent-namespace + parent-name) is still queryable via
    # `kubectl get -l openova.io/continuum-mirror-of-namespace=<ns>`
    # without forming an illegal value. Per
    # `feedback_inviolable_principles.md` #4 both halves stay
    # values-overridable.
    openova.io/continuum-mirror-of-namespace: {{ .Values.qaFixtures.namespace | default "qa-omantel" | quote }}
    openova.io/continuum-mirror-of-name: {{ .Values.qaFixtures.continuumName | default "cont-omantel" | quote }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
    app.kubernetes.io/instance: {{ .Release.Name }}
spec:
  applicationRef: {{ .Values.qaFixtures.appName | default "qa-wp" }}
  primaryRegion: {{ .Values.qaFixtures.primaryRegion | default "hz-fsn-rtz-prod" }}
  hotStandbyRegions:
    - {{ .Values.qaFixtures.standbyRegion | default "hz-hel-rtz-prod" }}
  rto: "60s"
  rpo: "30s"
  rpoSeconds: 30
  rtoSeconds: 60
  autoFailover: false
  cnpgPair:
    name: {{ .Values.qaFixtures.cnpgPairName | default "qa-cnpgpair" }}
    namespace: {{ .Values.qaFixtures.namespace | default "qa-omantel" }}
  pdmZone: "openova.io"
  leaseClient:
    kind: dns-quorum
    config:
      ttlSeconds: 30
      renewSeconds: 10
      resolvers:
        - "10.43.0.10"
        - "10.43.0.11"
        - "10.43.0.12"
  luaRecord:
    selector: ifurlup
    healthCheck:
      url: "https://{{ .Values.qaFixtures.appName | default "qa-wp" }}.{{ .Values.qaFixtures.publicHost | default "openova.io" }}/healthz"
      intervalSeconds: 5
      timeoutSeconds: 2
{{- end }}
