#!/bin/bash
set -euo pipefail

# =============================================================================
# generate-helix-services.sh
# -----------------------------------------------------------------------------
# Scaffolds dev/uat/prod CircleCI deployment configs for the new Helix (Odenza)
# services onboarding Lumen and Gescan.
#
# For each service and each environment (dev, uat, prod) it writes:
#   <env>/<service>/.circleci/config.yml
#   <env>/<service>/docker/Dockerfile        (+ any sibling docker/ files)
#   <env>/<service>/scripts/setup-kubectl.sh
#   <env>/<service>/scripts/deploy-services.sh
#   <env>/<service>/charts/<service>/Chart.yaml
#   <env>/<service>/charts/<service>/values.yaml
#   <env>/<service>/charts/<service>/templates/{deployment,service,ingress,secrets,hpa,pdb}.yaml
#   <env>/<service>/.github/workflows/manage-secrets.yml
#   <env>/<service>/README.md
#
# Conventions (verified against existing services + docs/):
#   - dev  -> branch develop, context devops-mgt,       bucket tbi-charts,      runner loadtest, MUTABLE tags
#   - uat  -> branch uat,     context devops-mgt,       bucket tbi-charts,      runner loadtest, MUTABLE tags
#   - prod -> branch master,  context devops-mgt-prod,  bucket tbi-charts-prod, runner prod-win, IMMUTABLE tags
#            + 2-stage manual approval workflow + SNS notifications
#   - Dockerfile/scripts are staged from THIS repo at build time by the pipeline.
#   - Helm values/secrets (hostName, acm_cert_arn, image, dotenv) live in the
#     separate tbi-environments repo, per <env>/values/<service>.yaml.
# =============================================================================

BASE_DIR="$(cd "$(dirname "$0")/.." && pwd)"
# Where the application repos were cloned (source of real Dockerfiles).
CLONE_DIR="${HELIX_CLONE_DIR:-${HOME}/Desktop/git-code/tbi-code/_helix-clones}"

NAMESPACE="helix"
# ECR account that hosts the shared PHP base images and app images.
GITHUB_ORG="redlabelvacations"

# Service definitions: SERVICE_NAME|REPO_NAME|TECH
#   TECH: php-node (composer + node asset build) | php (composer only)
SERVICES=(
  "tbi-hlx-dynamic-path|tbi-hlx-dynamic-path|php-node"
  "tbi-hlx-whitelabel-frontend|tbi-hlx-whitelabel-frontend|php-node"
  "tbi-hlx-booking-path|tbi-hlx-booking-path|php-node"
  "tbi-rlv-helix-bridge|tbi-rlv-helix-bridge|php"
  "tbi-legacy-hotels-service|tbi-legacy-hotels-service|php"
)

ENVIRONMENTS=(dev uat prod)

# Per-service, per-env ingress hostnames.
# Lumen and Gescan use a dev- prefix outside prod.
# TD, ATB MyRewards, and ATB MyBusinessRewards use their real staging and prod names.
# Only the three site-serving frontends have public hostnames; the bridge is
# an internal microservice (no public ingress). UAT is not a Helix target.
hostnames_for() {
  local svc="$1" env="$2"
  local prefix=""
  [ "$env" != "prod" ] && prefix="dev-"
  case "$svc" in
    tbi-hlx-dynamic-path)
      if [ "$env" = "prod" ]; then
        echo "${prefix}res-recompenses.lumen.ca ${prefix}res-rewards.gescan.com dyn-path-td-odenza.redtag.ca dyn-path-atb-odenza.redtag.ca dyn-path-atbbusiness-odenza.redtag.ca"
      elif [ "$env" = "dev" ]; then
        echo "${prefix}res-recompenses.lumen.ca ${prefix}res-rewards.gescan.com hotels-td-odenza.stg.travelco.com hotels-atb-odenza.stg.travelco.com hotels-atbbusiness-odenza.stg.travelco.com"
      else
        echo "${prefix}res-recompenses.lumen.ca ${prefix}res-rewards.gescan.com"
      fi
      ;;
    tbi-hlx-booking-path)
      if [ "$env" = "prod" ]; then
        echo "${prefix}rev-recompenses.lumen.ca ${prefix}rev-rewards.gescan.com book-path-td-odenza.redtag.ca book-path-atb-odenza.redtag.ca book-path-atbbusiness-odenza.redtag.ca"
      elif [ "$env" = "dev" ]; then
        echo "${prefix}rev-recompenses.lumen.ca ${prefix}rev-rewards.gescan.com flights-td-odenza.stg.travelco.com flights-atb-odenza.stg.travelco.com flights-atbbusiness-odenza.stg.travelco.com"
      else
        echo "${prefix}rev-recompenses.lumen.ca ${prefix}rev-rewards.gescan.com"
      fi
      ;;
    tbi-hlx-whitelabel-frontend)
      if [ "$env" = "prod" ]; then
        echo "${prefix}travel-recompenses.lumen.ca ${prefix}travel-rewards.gescan.com travel.tdreferralrewards.com td-odenza.redtag.ca travel.atbmyrewards.com atb-odenza.redtag.ca travel.atbmybusinessrewards.com atbbusiness-odenza.redtag.ca"
      elif [ "$env" = "dev" ]; then
        echo "${prefix}travel-recompenses.lumen.ca ${prefix}travel-rewards.gescan.com td-odenza.stg.travelco.com atb-odenza.stg.travelco.com atbbusiness-odenza.stg.travelco.com"
      else
        echo "${prefix}travel-recompenses.lumen.ca ${prefix}travel-rewards.gescan.com"
      fi
      ;;
    *) echo "" ;;
  esac
}

env_settings() {
  # Emits: BRANCH CONTEXT S3_BUCKET RUNNER TAG_MUTABILITY DEPLOY_ENV
  case "$1" in
    dev)  echo "develop devops-mgt tbi-charts loadtest MUTABLE dev" ;;
    uat)  echo "uat devops-mgt tbi-charts loadtest MUTABLE uat" ;;
    prod) echo "master devops-mgt-prod tbi-charts-prod prod-win IMMUTABLE prod" ;;
  esac
}

# -----------------------------------------------------------------------------
# Dockerfile: copy the real one from the cloned app repo, normalizing the base
# image FROM to the fully-qualified ECR path for the TARGET environment's
# account:
#   dev          -> 594765360358 (tbi-dev ECR)
#   uat / prod    -> 489176460030 (tbi-prod / shared ECR)
# -----------------------------------------------------------------------------
stage_dockerfile() {
  local svc="$1" repo="$2" dest_dir="$3" env="$4"
  local src="${CLONE_DIR}/${repo}/Dockerfile"
  local base_acct="489176460030"
  [ "$env" = "dev" ] && base_acct="594765360358"
  local base_reg="${base_acct}.dkr.ecr.ca-central-1.amazonaws.com"
  mkdir -p "$dest_dir"
  if [ ! -f "$src" ]; then
    echo "    WARNING: no Dockerfile in ${repo}; writing placeholder"
    echo "# TODO: Dockerfile missing from ${repo}" > "${dest_dir}/Dockerfile"
    return
  fi
  # Normalize the base-image FROM line to the target env's ECR account,
  # whether it was unqualified (tbi-php-XX:latest) or already pointed at a
  # specific account.
  sed -E \
    -e "s#^FROM[[:space:]]+tbi-php-85:latest#FROM ${base_reg}/tbi-php-85:latest#" \
    -e "s#^FROM[[:space:]]+tbi-php-81:latest#FROM ${base_reg}/tbi-php-81:latest#" \
    -e "s#^FROM[[:space:]]+tbi-php-73:latest#FROM ${base_reg}/tbi-php-73:latest#" \
    -e "s#^FROM[[:space:]]+[0-9]+\.dkr\.ecr\.ca-central-1\.amazonaws\.com/tbi-php-#FROM ${base_reg}/tbi-php-#" \
    "$src" > "${dest_dir}/Dockerfile"
  # Copy any sibling docker/ assets (php.ini, opcache.ini, nginx.conf, etc.).
  if [ -d "${CLONE_DIR}/${repo}/docker" ]; then
    cp -R "${CLONE_DIR}/${repo}/docker/." "${dest_dir}/"
  fi
}

# -----------------------------------------------------------------------------
# setup-kubectl.sh
# -----------------------------------------------------------------------------
write_setup_kubectl() {
  cat > "$1" <<'EOS'
#!/bin/bash
set -e

echo "=== Setting up kubectl and helm plugins ==="

if ! command -v kubectl &> /dev/null; then
    curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
    sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
    rm kubectl
fi

CLUSTER_NAME="${CLUSTER_NAME:-dev}"
AWS_REGION="${AWS_REGION:-ca-central-1}"
NAMESPACE="${NAMESPACE:-helix}"

echo "Getting kubeconfig for cluster: ${CLUSTER_NAME} in region: ${AWS_REGION}"
aws eks update-kubeconfig --name "${CLUSTER_NAME}" --region "${AWS_REGION}"

kubectl cluster-info
if ! kubectl get namespace "${NAMESPACE}" &> /dev/null; then
    echo "Creating ${NAMESPACE} namespace..."
    kubectl create namespace "${NAMESPACE}"
fi

if ! helm plugin list | grep -q secrets; then
    helm plugin install https://github.com/jkroepke/helm-secrets --version v4.5.1
fi
if ! helm plugin list | grep -q s3; then
    helm plugin install https://github.com/hypnoglow/helm-s3.git
fi

if ! command -v sops &> /dev/null; then
    wget https://github.com/mozilla/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64 -O /tmp/sops
    sudo mv /tmp/sops /usr/local/bin/sops
    sudo chmod +x /usr/local/bin/sops
fi

export AWS_REGION="${AWS_REGION:-ca-central-1}"
unset AWS_PROFILE || true

echo "AWS Account ID: $(aws sts get-caller-identity --query Account --output text)"
echo "=== kubectl and helm setup complete ==="
EOS
}

# -----------------------------------------------------------------------------
# deploy-services.sh (parameterized per service/namespace/env/bucket)
# -----------------------------------------------------------------------------
write_deploy_services() {
  local out="$1" svc="$2" ns="$3" env="$4" bucket="$5"
  cat > "$out" <<EOS
#!/bin/bash
set -e

# Deployment script for ${svc} (${env}).
# Values and secrets are sourced from the tbi-environments repo at deploy time.

SERVICE_NAME="${svc}"
NAMESPACE="${ns}"
ENVIRONMENT="\${DEPLOY_ENV:-${env}}"
AWS_REGION="\${AWS_REGION:-ca-central-1}"
TBI_ENVIRONMENTS_BRANCH="\${TBI_ENVIRONMENTS_BRANCH:-master}"
HELM_S3_BUCKET="\${HELM_S3_BUCKET:-${bucket}}"
EOS
  cat >> "$out" <<'EOS'

helm_s3_push_with_retry() {
    local chart_file="$1"; local repo_name="$2"
    local max_retries=5 attempt=1 wait_time=5
    while [ $attempt -le $max_retries ]; do
        if helm s3 push "${chart_file}" "${repo_name}" --force 2>&1; then return 0; fi
        echo "helm s3 push failed (attempt ${attempt}/${max_retries}), retrying in ${wait_time}s..."
        sleep $wait_time; wait_time=$((wait_time * 2)); attempt=$((attempt + 1))
    done
    echo "ERROR: helm s3 push failed after ${max_retries} attempts"; return 1
}

git_push_with_retry() {
    local branch="${1:-master}"; local max_retries=5 attempt=1
    while [ $attempt -le $max_retries ]; do
        if git push origin "$branch" 2>&1; then return 0; fi
        echo "Push failed (attempt ${attempt}/${max_retries}), rebasing and retrying..."
        git pull --rebase origin "$branch"; attempt=$((attempt + 1))
    done
    echo "ERROR: Failed to push after ${max_retries} attempts"; return 1
}

for v in CLUSTER_NAME AWS_ACCOUNT_ID GITHUB_TOKEN; do
    if [ -z "${!v:-}" ]; then echo "ERROR: ${v} is not set!"; exit 1; fi
done

TBI_ENVIRONMENTS_REPO="https://github.com/redlabelvacations/tbi-environments.git"

if [ -f "IMAGE_TAG" ]; then
    CHART_VERSION=$(cat IMAGE_TAG | tr -d '[:space:]')
else
    BASE_VERSION=$(cat VERSION 2>/dev/null | tr -d '[:space:]' || echo "1.0.0")
    CHART_VERSION="${BASE_VERSION}-${CIRCLE_BRANCH}-${CIRCLE_BUILD_NUM}"
fi

echo "Service: ${SERVICE_NAME} | Env: ${ENVIRONMENT} | Namespace: ${NAMESPACE} | Version: ${CHART_VERSION}"

chart_path="tbi-devops-environments/${ENVIRONMENT}/${SERVICE_NAME}/charts/${SERVICE_NAME}"
[ -d "${chart_path}" ] || { echo "ERROR: chart not found: ${chart_path}"; exit 1; }

echo "=== Packaging Helm chart ==="
sed -i.bak "s|version:.*|version: ${CHART_VERSION}|g" ${chart_path}/Chart.yaml
sed -i.bak "s|appVersion:.*|appVersion: \"${CHART_VERSION}\"|g" ${chart_path}/Chart.yaml
rm -f ${chart_path}/Chart.yaml.bak
mkdir -p /tmp/helm-charts
helm package ${chart_path} -d /tmp/helm-charts

echo "=== Pushing chart to S3 repo (${HELM_S3_BUCKET}) ==="
helm repo add tbi-charts s3://${HELM_S3_BUCKET}/
helm_s3_push_with_retry "/tmp/helm-charts/${SERVICE_NAME}-${CHART_VERSION}.tgz" tbi-charts

echo "=== Cloning tbi-environments ==="
rm -rf /tmp/tbi-environments
# Authenticate via a credential helper (survives GitHub's redirect, unlike an
# inline token in the URL), and persist it on the clone so the later push works.
git -c credential.helper= \
    -c credential.helper='!f() { echo "username=x-access-token"; echo "password=${GITHUB_TOKEN}"; }; f' \
    clone "${TBI_ENVIRONMENTS_REPO}" /tmp/tbi-environments
git -C /tmp/tbi-environments config credential.helper '!f() { echo "username=x-access-token"; echo "password=${GITHUB_TOKEN}"; }; f'
cd /tmp/tbi-environments
git checkout ${TBI_ENVIRONMENTS_BRANCH}

image_url="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${SERVICE_NAME}:${CHART_VERSION}"
values_file="${ENVIRONMENT}/values/${SERVICE_NAME}.yaml"
if [ -f "${values_file}" ]; then
    sed -i.bak "s|^image:.*|image: ${image_url}|g" "${values_file}"; rm -f "${values_file}.bak"
    echo "Updated ${values_file} -> ${image_url}"
else
    echo "WARNING: ${values_file} not found in tbi-environments; create it before deploy."
fi

git config user.name "CircleCI Bot"
git config user.email "circleci@tbidevelopment.ca"
git add "${values_file}" || true
if git diff --cached --quiet; then
    echo "No values changes to commit."
else
    git commit -m "Update ${SERVICE_NAME} image to ${CHART_VERSION} (build #${CIRCLE_BUILD_NUM})"
    git_push_with_retry "${TBI_ENVIRONMENTS_BRANCH}"
fi

echo "=== Deploying with helm secrets ==="
helm repo update
helm secrets upgrade --install \
    --namespace ${NAMESPACE} ${SERVICE_NAME} \
    tbi-charts/${SERVICE_NAME} \
    --set cluster="${CLUSTER_NAME}" \
    --version ${CHART_VERSION} \
    --reset-values \
    -f ./${ENVIRONMENT}/common.yaml \
    -f ./${ENVIRONMENT}/secrets.common.yaml \
    -f ./${ENVIRONMENT}/values/${SERVICE_NAME}.yaml \
    -f ./${ENVIRONMENT}/secrets/secrets.${SERVICE_NAME}.yaml

kubectl rollout status deployment/${SERVICE_NAME} -n ${NAMESPACE} --timeout=5m
echo "=== Deployment complete: ${CHART_VERSION} ==="
EOS
}

# -----------------------------------------------------------------------------
# Helm chart templates
# -----------------------------------------------------------------------------
write_chart() {
  local chart_dir="$1" svc="$2" has_ingress="$3"
  mkdir -p "${chart_dir}/templates"

  cat > "${chart_dir}/Chart.yaml" <<EOF
apiVersion: v2
name: ${svc}
description: Helm chart for ${svc} (Helix / Odenza - PHP Apache)
type: application
version: 1.0.0
appVersion: "1.0.0"
EOF

  cat > "${chart_dir}/values.yaml" <<'EOF'
# Default values are a placeholder. Real values (name, image, hostName,
# acm_cert_arn, configs.dotenv) are supplied at deploy time from the
# tbi-environments repo: <env>/values/<service>.yaml and secrets.<service>.yaml
EOF

  cat > "${chart_dir}/templates/deployment.yaml" <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.name }}
  labels:
    app: {{ .Values.name }}
    group: {{ .Values.group | default "helix" }}
spec:
  replicas: {{ .Values.replicas | default 1 }}
  selector:
    matchLabels:
      app: {{ .Values.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.name }}
        group: {{ .Values.group | default "helix" }}
    spec:
      containers:
      - name: web
        image: {{ .Values.image }}
        imagePullPolicy: Always
        ports:
        - containerPort: {{ .Values.targetPort | default 80 }}
        volumeMounts:
          - name: dotenv
            mountPath: /data/websites/.env
            subPath: .env
        resources:
          requests:
            memory: {{ .Values.resources.requests.memory | default "256Mi" | quote }}
            cpu: {{ .Values.resources.requests.cpu | default "100m" | quote }}
          limits:
            memory: {{ .Values.resources.limits.memory | default "512Mi" | quote }}
            cpu: {{ .Values.resources.limits.cpu | default "500m" | quote }}
      volumes:
        - name: dotenv
          secret:
            secretName: {{ .Values.name }}-secrets
            items:
              - key: dotenv
                path: .env
EOF

  cat > "${chart_dir}/templates/service.yaml" <<'EOF'
kind: Service
apiVersion: v1
metadata:
  namespace: {{ .Release.Namespace }}
  name: {{ .Values.name }}
spec:
  type: {{ .Values.service_type | default "ClusterIP" }}
  ports:
  - name: http
    port: {{ .Values.port | default 80 }}
    targetPort: {{ .Values.targetPort | default 80 }}
    protocol: TCP
  selector:
    app: {{ .Values.name }}
EOF

  if [ "$has_ingress" = "true" ]; then
    cat > "${chart_dir}/templates/ingress.yaml" <<'EOF'
{{- if .Values.ingress_enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Values.name }}-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: {{ .Values.alb_scheme | default "internet-facing" }}
    alb.ingress.kubernetes.io/target-type: ip
    # Pre-provisioned ACM certificate ARN(s) for the Lumen/Gescan domain(s).
    alb.ingress.kubernetes.io/certificate-arn: {{ .Values.acm_cert_arn }}
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80},{"HTTPS":443}]'
    alb.ingress.kubernetes.io/ssl-redirect: '443'
    alb.ingress.kubernetes.io/healthcheck-path: {{ .Values.healthcheck_path | default "/" }}
spec:
  rules:
  {{- range .Values.hosts }}
  - host: {{ . }}
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: {{ $.Values.name }}
            port:
              number: {{ $.Values.port | default 80 }}
  {{- end }}
{{- end }}
EOF
  fi

  cat > "${chart_dir}/templates/secrets.yaml" <<'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: {{ .Values.name }}-secrets
type: Opaque
data:
  dotenv: "{{ .Values.configs.dotenv | b64enc }}"
EOF

  cat > "${chart_dir}/templates/hpa.yaml" <<'EOF'
{{- if .Values.autoscaling }}
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: {{ .Values.name }}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ .Values.name }}
  minReplicas: {{ .Values.autoscaling.minReplicas | default 1 }}
  maxReplicas: {{ .Values.autoscaling.maxReplicas | default 3 }}
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage | default 70 }}
{{- end }}
{{- end }}
EOF

  cat > "${chart_dir}/templates/pdb.yaml" <<'EOF'
{{- if .Values.podDisruptionBudget }}
{{- if .Values.podDisruptionBudget.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: {{ .Values.name }}
spec:
  minAvailable: {{ .Values.podDisruptionBudget.minAvailable | default 1 }}
  selector:
    matchLabels:
      app: {{ .Values.name }}
{{- end }}
{{- end }}
EOF
}

# -----------------------------------------------------------------------------
# .github/workflows/manage-secrets.yml
# -----------------------------------------------------------------------------
write_manage_secrets() {
  local out="$1" svc="$2" ns="$3"
  cat > "$out" <<EOF
name: Manage Kubernetes Secrets

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options:
          - dev
          - uat
          - prod
      action:
        description: 'Action to perform'
        required: true
        type: choice
        options:
          - view
          - update
          - update-and-restart

jobs:
  manage-secrets:
    runs-on: ubuntu-latest
    environment: \${{ github.event.inputs.environment }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: \${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: \${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ca-central-1
      - name: Configure kubectl
        run: aws eks update-kubeconfig --name \${{ secrets.EKS_CLUSTER_NAME }} --region ca-central-1
      - name: View secret
        if: github.event.inputs.action == 'view'
        run: kubectl get secret ${svc}-secrets --namespace=${ns} -o yaml
      - name: Restart deployment
        if: github.event.inputs.action == 'update-and-restart'
        run: kubectl rollout restart deployment/${svc} --namespace=${ns}
EOF
}

# -----------------------------------------------------------------------------
# .circleci/config.yml — non-prod (dev/uat): 3-job auto chain
# -----------------------------------------------------------------------------
write_circleci_nonprod() {
  local out="$1" svc="$2" ns="$3" env="$4" branch="$5" context="$6" bucket="$7"
  cat > "$out" <<EOF
version: 2.1

orbs:
  aws-cli: circleci/aws-cli@5.4.1
  helm: circleci/helm@3.2.0

jobs:
  lint-and-test:
    docker:
      - image: cimg/php:8.3-node
    resource_class: small
    steps:
      - checkout
      - run:
          name: Install Composer dependencies
          command: composer install --no-interaction --prefer-dist || echo "composer install completed with warnings"
      - run:
          name: Validate composer.json
          command: composer validate --strict || echo "composer validation warnings"
      - run:
          name: Generate VERSION tag
          command: |
            BASE_VERSION=\$(cat VERSION 2>/dev/null | tr -d '[:space:]' || echo "1.0.0")
            echo "Image tag will be: \${BASE_VERSION}-\${CIRCLE_BRANCH}-\${CIRCLE_BUILD_NUM}"
            echo "\${BASE_VERSION}" > VERSION
      - persist_to_workspace:
          root: .
          paths:
            - VERSION

  build-and-push-images:
    docker:
      - image: cimg/aws:2023.09
    resource_class: medium
    steps:
      - checkout
      - attach_workspace:
          at: .
      - setup_remote_docker:
          docker_layer_caching: true
      - run:
          name: Stage Dockerfile from tbi-devops-environments
          command: |
            set -x
            # Clear any credential helper CircleCI's checkout installed (scoped to
            # this project repo) and authenticate this clone with GITHUB_TOKEN directly.
            git -c credential.helper= \\
                -c credential.helper='!f() { echo "username=x-access-token"; echo "password=\${GITHUB_TOKEN}"; }; f' \\
                clone https://github.com/${GITHUB_ORG}/tbi-devops-environments \\
                -b master --single-branch tbi-devops-environments
            case \${CIRCLE_BRANCH} in
              develop) envFolder="dev" ;;
              uat) envFolder="uat" ;;
              master|main) envFolder="prod" ;;
              *) envFolder="${env}" ;;
            esac
            dockerDir="tbi-devops-environments/\${envFolder}/${svc}/docker"
            if [ -f "\$dockerDir/Dockerfile" ]; then
              cp -R \$dockerDir/. ./
              echo "Dockerfile staged from \$dockerDir"
            else
              echo "ERROR: Dockerfile not found at \$dockerDir/Dockerfile"; exit 1
            fi
            set +x
      - run:
          name: Generate image tag
          command: |
            BASE_VERSION=\$(cat VERSION | tr -d '[:space:]')
            IMAGE_TAG="\${BASE_VERSION}-\${CIRCLE_BRANCH}-\${CIRCLE_BUILD_NUM}"
            echo "export IMAGE_TAG=\${IMAGE_TAG}" >> \$BASH_ENV
            echo "\${IMAGE_TAG}" > IMAGE_TAG
      - persist_to_workspace:
          root: .
          paths:
            - IMAGE_TAG
      - run:
          name: Login to AWS ECR
          command: |
            aws ecr get-login-password --region \${AWS_REGION} | \\
            docker login --username AWS --password-stdin \${AWS_ACCOUNT_ID}.dkr.ecr.\${AWS_REGION}.amazonaws.com
      - run:
          name: Build and push Docker image
          command: |
            SERVICE="${svc}"
            ECR_REGISTRY="\${AWS_ACCOUNT_ID}.dkr.ecr.\${AWS_REGION}.amazonaws.com"
            aws ecr describe-repositories --repository-names \${SERVICE} --region \${AWS_REGION} || \\
              aws ecr create-repository --repository-name \${SERVICE} \\
                --image-tag-mutability MUTABLE --region \${AWS_REGION}
            docker build -t \${SERVICE}:\${IMAGE_TAG} .
            docker tag \${SERVICE}:\${IMAGE_TAG} \${ECR_REGISTRY}/\${SERVICE}:\${IMAGE_TAG}
            docker push \${ECR_REGISTRY}/\${SERVICE}:\${IMAGE_TAG}

  deploy-to-kubernetes:
    executor: aws-cli/default
    resource_class: tbi-local-runner/loadtest
    steps:
      - checkout
      - attach_workspace:
          at: .
      - aws-cli/setup:
          profile_name: default
      - helm/install_helm_client:
          version: v3.8.2
      - run:
          name: Stage deployment scripts from tbi-devops-environments
          command: |
            # Clear any credential helper CircleCI's checkout installed (scoped to
            # this project repo) and authenticate this clone with GITHUB_TOKEN directly.
            git -c credential.helper= \\
                -c credential.helper='!f() { echo "username=x-access-token"; echo "password=\${GITHUB_TOKEN}"; }; f' \\
                clone https://github.com/${GITHUB_ORG}/tbi-devops-environments \\
                -b master --single-branch tbi-devops-environments
            case \${CIRCLE_BRANCH} in
              develop) envFolder="dev" ;;
              uat) envFolder="uat" ;;
              master|main) envFolder="prod" ;;
              *) envFolder="${env}" ;;
            esac
            mkdir -p scripts
            cp -v tbi-devops-environments/\${envFolder}/${svc}/scripts/*.sh scripts/
      - run:
          name: Setup kubectl and helm plugins
          command: |
            chmod +x scripts/setup-kubectl.sh
            NAMESPACE=${ns} bash scripts/setup-kubectl.sh
      - run:
          name: Deploy to Kubernetes
          command: |
            chmod +x scripts/deploy-services.sh
            bash scripts/deploy-services.sh

workflows:
  deploy-${svc}:
    jobs:
      - lint-and-test:
          context: ${context}
          filters:
            branches:
              only:
                - ${branch}
      - build-and-push-images:
          context: ${context}
          requires:
            - lint-and-test
          filters:
            branches:
              only:
                - ${branch}
      - deploy-to-kubernetes:
          context: ${context}
          requires:
            - build-and-push-images
          filters:
            branches:
              only:
                - ${branch}
EOF
}

# -----------------------------------------------------------------------------
# .circleci/config.yml — prod: 2-stage approval + SNS
# -----------------------------------------------------------------------------
write_circleci_prod() {
  local out="$1" svc="$2" ns="$3"
  cat > "$out" <<EOF
version: 2.1

orbs:
  aws-cli: circleci/aws-cli@5.4.1
  helm: circleci/helm@3.2.0

jobs:
  lint-and-test:
    docker:
      - image: cimg/php:8.3-node
    resource_class: small
    steps:
      - checkout
      - run:
          name: Install Composer dependencies
          command: composer install --no-interaction --prefer-dist || echo "composer install completed with warnings"
      - run:
          name: Validate composer.json
          command: composer validate --strict || echo "composer validation warnings"
      - persist_to_workspace:
          root: .
          paths:
            - VERSION

  build-and-push-images:
    docker:
      - image: cimg/aws:2023.09
    resource_class: medium
    steps:
      - checkout
      - attach_workspace:
          at: .
      - setup_remote_docker:
          docker_layer_caching: true
      - run:
          name: Stage Dockerfile from tbi-devops-environments
          command: |
            set -x
            # Clear any credential helper CircleCI's checkout installed (scoped to
            # this project repo) and authenticate this clone with GITHUB_TOKEN directly.
            git -c credential.helper= \\
                -c credential.helper='!f() { echo "username=x-access-token"; echo "password=\${GITHUB_TOKEN}"; }; f' \\
                clone https://github.com/${GITHUB_ORG}/tbi-devops-environments \\
                -b master --single-branch tbi-devops-environments
            dockerDir="tbi-devops-environments/prod/${svc}/docker"
            if [ -f "\$dockerDir/Dockerfile" ]; then
              cp -R \$dockerDir/. ./
            else
              echo "ERROR: Dockerfile not found at \$dockerDir/Dockerfile"; exit 1
            fi
            set +x
      - run:
          name: Generate clean image tag
          command: |
            BASE_VERSION=\$(cat VERSION | tr -d '[:space:]')
            echo "export IMAGE_TAG=\${BASE_VERSION}" >> \$BASH_ENV
            echo "\${BASE_VERSION}" > IMAGE_TAG
      - persist_to_workspace:
          root: .
          paths:
            - IMAGE_TAG
      - run:
          name: Login to AWS ECR
          command: |
            aws ecr get-login-password --region \${AWS_REGION} | \\
            docker login --username AWS --password-stdin \${AWS_ACCOUNT_ID}.dkr.ecr.\${AWS_REGION}.amazonaws.com
      - run:
          name: Build and push Docker image (IMMUTABLE)
          command: |
            SERVICE="${svc}"
            ECR_REGISTRY="\${AWS_ACCOUNT_ID}.dkr.ecr.\${AWS_REGION}.amazonaws.com"
            aws ecr describe-repositories --repository-names \${SERVICE} --region \${AWS_REGION} || \\
              aws ecr create-repository --repository-name \${SERVICE} \\
                --image-tag-mutability IMMUTABLE --region \${AWS_REGION}
            docker build -t \${SERVICE}:\${IMAGE_TAG} .
            docker tag \${SERVICE}:\${IMAGE_TAG} \${ECR_REGISTRY}/\${SERVICE}:\${IMAGE_TAG}
            docker push \${ECR_REGISTRY}/\${SERVICE}:\${IMAGE_TAG}

  notify-await-build-approval:
    executor: aws-cli/default
    resource_class: small
    steps:
      - checkout
      - aws-cli/setup:
          profile_name: default
      - run:
          name: Send SNS notification - Awaiting BUILD approval
          command: |
            BASE_VERSION=\$(cat VERSION | tr -d '[:space:]')
            cat > /tmp/sns-message.txt \<< MSG
            PRODUCTION Pipeline - Awaiting BUILD Approval
            Service: ${svc}
            Environment: PRODUCTION
            Namespace: ${ns}
            Build: #\${CIRCLE_BUILD_NUM}
            Image Tag: \${BASE_VERSION}
            Approve at: \${CIRCLE_BUILD_URL}
            MSG
            aws sns publish --region ca-central-1 \\
              --topic-arn "arn:aws:sns:ca-central-1:489176460030:DevOps-alert" \\
              --subject "PROD Approval Needed: BUILD - ${svc} #\${CIRCLE_BUILD_NUM}" \\
              --message file:///tmp/sns-message.txt

  notify-await-deploy-approval:
    executor: aws-cli/default
    resource_class: small
    steps:
      - attach_workspace:
          at: .
      - aws-cli/setup:
          profile_name: default
      - run:
          name: Send SNS notification - Awaiting DEPLOY approval
          command: |
            IMAGE_TAG=\$(cat IMAGE_TAG | tr -d '[:space:]')
            cat > /tmp/sns-message.txt \<< MSG
            PRODUCTION Pipeline - Awaiting DEPLOY Approval
            Service: ${svc}
            Environment: PRODUCTION
            Namespace: ${ns}
            Build: #\${CIRCLE_BUILD_NUM}
            Image Tag: \${IMAGE_TAG}
            This will deploy to the PRODUCTION cluster.
            Approve at: \${CIRCLE_BUILD_URL}
            MSG
            aws sns publish --region ca-central-1 \\
              --topic-arn "arn:aws:sns:ca-central-1:489176460030:DevOps-alert" \\
              --subject "PROD Approval Needed: DEPLOY - ${svc} #\${CIRCLE_BUILD_NUM}" \\
              --message file:///tmp/sns-message.txt

  deploy-to-kubernetes:
    executor: aws-cli/default
    resource_class: tbi-local-runner/prod-win
    steps:
      - checkout
      - attach_workspace:
          at: .
      - aws-cli/setup:
          profile_name: default
      - helm/install_helm_client:
          version: v3.8.2
      - run:
          name: Verify production environment
          command: |
            if [ "\${DEPLOY_ENV}" != "prod" ]; then
              echo "ERROR: DEPLOY_ENV must be 'prod' (got '\${DEPLOY_ENV}')"; exit 1
            fi
      - run:
          name: Stage deployment scripts from tbi-devops-environments
          command: |
            # Clear any credential helper CircleCI's checkout installed (scoped to
            # this project repo) and authenticate this clone with GITHUB_TOKEN directly.
            git -c credential.helper= \\
                -c credential.helper='!f() { echo "username=x-access-token"; echo "password=\${GITHUB_TOKEN}"; }; f' \\
                clone https://github.com/${GITHUB_ORG}/tbi-devops-environments \\
                -b master --single-branch tbi-devops-environments
            mkdir -p scripts
            cp -v tbi-devops-environments/prod/${svc}/scripts/*.sh scripts/
      - run:
          name: Setup kubectl and helm plugins
          command: |
            chmod +x scripts/setup-kubectl.sh
            NAMESPACE=${ns} bash scripts/setup-kubectl.sh
      - run:
          name: Deploy to Kubernetes
          command: |
            chmod +x scripts/deploy-services.sh
            bash scripts/deploy-services.sh
      - run:
          name: Send deployment success notification
          command: |
            IMAGE_TAG=\$(cat IMAGE_TAG | tr -d '[:space:]')
            cat > /tmp/sns-success.txt \<< MSG
            Deployment Successful - ${svc}
            Environment: PRODUCTION
            Namespace: ${ns}
            Image Tag: \${IMAGE_TAG}
            Build: #\${CIRCLE_BUILD_NUM}
            MSG
            aws sns publish --region ca-central-1 \\
              --topic-arn "arn:aws:sns:ca-central-1:489176460030:DevOps-alert" \\
              --subject "PROD Deploy Success - ${svc} #\${CIRCLE_BUILD_NUM}" \\
              --message file:///tmp/sns-success.txt
      - run:
          name: Send deployment failure notification
          when: on_fail
          command: |
            cat > /tmp/sns-fail.txt \<< MSG
            Deployment FAILED - ${svc}
            Environment: PRODUCTION
            Namespace: ${ns}
            Build: #\${CIRCLE_BUILD_NUM}
            Check logs: \${CIRCLE_BUILD_URL}
            MSG
            aws sns publish --region ca-central-1 \\
              --topic-arn "arn:aws:sns:ca-central-1:489176460030:DevOps-alert" \\
              --subject "PROD Deploy FAILED - ${svc} #\${CIRCLE_BUILD_NUM}" \\
              --message file:///tmp/sns-fail.txt

workflows:
  deploy-${svc}:
    jobs:
      - lint-and-test:
          context: devops-mgt-prod
          filters:
            branches:
              only:
                - master
      - notify-await-build-approval:
          context: devops-mgt-prod
          requires:
            - lint-and-test
          filters:
            branches:
              only:
                - master
      - approve-build:
          type: approval
          requires:
            - notify-await-build-approval
          filters:
            branches:
              only:
                - master
      - build-and-push-images:
          context: devops-mgt-prod
          requires:
            - approve-build
          filters:
            branches:
              only:
                - master
      - notify-await-deploy-approval:
          context: devops-mgt-prod
          requires:
            - build-and-push-images
          filters:
            branches:
              only:
                - master
      - approve-deploy:
          type: approval
          requires:
            - notify-await-deploy-approval
          filters:
            branches:
              only:
                - master
      - deploy-to-kubernetes:
          context: devops-mgt-prod
          requires:
            - approve-deploy
          filters:
            branches:
              only:
                - master
EOF
}

# -----------------------------------------------------------------------------
# README.md
# -----------------------------------------------------------------------------
write_readme() {
  local out="$1" svc="$2" ns="$3" env="$4" branch="$5" context="$6" bucket="$7" hosts="$8"
  {
    echo "# ${svc} (${env})"
    echo
    echo "CircleCI deployment configuration for \`${svc}\` in the **${env}** environment."
    echo
    echo "| Setting | Value |"
    echo "|---------|-------|"
    echo "| Namespace | ${ns} |"
    echo "| Branch | ${branch} |"
    echo "| CircleCI context | ${context} |"
    echo "| Helm S3 bucket | ${bucket} |"
    if [ "$env" = "prod" ]; then
      echo "| Approval | 2-stage (approve-build + approve-deploy) |"
      echo "| Image tags | IMMUTABLE, clean VERSION |"
    else
      echo "| Approval | none (auto deploy) |"
      echo "| Image tags | MUTABLE, VERSION-branch-build |"
    fi
    echo
    if [ -n "$hosts" ]; then
      echo "## Deployment targets (hostnames)"
      echo
      for h in $hosts; do echo "- https://${h}"; done
      echo
      echo "> Hostnames and the pre-provisioned ACM certificate ARN(s) are supplied"
      echo "> per environment from \`tbi-environments/${env}/values/${svc}.yaml\`"
      echo "> (keys: \`hosts\`, \`acm_cert_arn\`, \`ingress_enabled: true\`)."
    else
      echo "## Deployment"
      echo
      echo "Internal microservice — no public ingress. ClusterIP service only."
    fi
    echo
    echo "## Files"
    echo "- \`.circleci/config.yml\` — pipeline definition"
    echo "- \`docker/Dockerfile\` — image build (base image from ECR \`tbi-php-*\`)"
    echo "- \`scripts/\` — \`setup-kubectl.sh\`, \`deploy-services.sh\`"
    echo "- \`charts/${svc}/\` — Helm chart"
    echo "- \`.github/workflows/manage-secrets.yml\` — secrets management"
  } > "$out"
}

# =============================================================================
# Main
# =============================================================================
echo "Generating Helix service configs under ${BASE_DIR}"
echo "Clone source: ${CLONE_DIR}"
echo

for entry in "${SERVICES[@]}"; do
  IFS='|' read -r svc repo tech <<< "$entry"
  for env in "${ENVIRONMENTS[@]}"; do
    read -r branch context bucket runner mutability deploy_env <<< "$(env_settings "$env")"
    hosts="$(hostnames_for "$svc" "$env")"
    has_ingress="false"; [ -n "$hosts" ] && has_ingress="true"

    svc_dir="${BASE_DIR}/${env}/${svc}"
    echo "  → ${env}/${svc} (ns=${NAMESPACE}, branch=${branch}, ingress=${has_ingress})"

    mkdir -p "${svc_dir}/.circleci" \
             "${svc_dir}/docker" \
             "${svc_dir}/scripts" \
             "${svc_dir}/charts/${svc}/templates" \
             "${svc_dir}/.github/workflows"

    stage_dockerfile "$svc" "$repo" "${svc_dir}/docker" "$env"
    write_setup_kubectl "${svc_dir}/scripts/setup-kubectl.sh"
    write_deploy_services "${svc_dir}/scripts/deploy-services.sh" "$svc" "$NAMESPACE" "$env" "$bucket"
    chmod +x "${svc_dir}/scripts/"*.sh
    write_chart "${svc_dir}/charts/${svc}" "$svc" "$has_ingress"
    write_manage_secrets "${svc_dir}/.github/workflows/manage-secrets.yml" "$svc" "$NAMESPACE"
    write_readme "${svc_dir}/README.md" "$svc" "$NAMESPACE" "$env" "$branch" "$context" "$bucket" "$hosts"

    if [ "$env" = "prod" ]; then
      write_circleci_prod "${svc_dir}/.circleci/config.yml" "$svc" "$NAMESPACE"
    else
      write_circleci_nonprod "${svc_dir}/.circleci/config.yml" "$svc" "$NAMESPACE" "$env" "$branch" "$context" "$bucket"
    fi
  done
done

echo
echo "Done. Generated $(( ${#SERVICES[@]} * ${#ENVIRONMENTS[@]} )) service directories."
