# Phase 36 Step 12 — build images, migrate, deploy on version tags.
# Requires act_runner with docker, curl, ssh, scp, bash and label ubuntu-latest.
#
# Secrets: REGISTRY_USERNAME, REGISTRY_TOKEN, PG_CONNECTION_DDL,
#          DEPLOY_HOST, DEPLOY_USER, DEPLOY_SSH_KEY
# Variables: PROD_API_URL, REGISTRY (optional; defaults below)
name: CD

on:
  push:
    tags: ["v*"]
  workflow_dispatch:
    inputs:
      image_tag:
        description: "Image tag to deploy (defaults to the pushed tag)"
        required: false

env:
  REGISTRY: gitea.example.com/vigilcare

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    outputs:
      tag: ${{ steps.meta.outputs.tag }}
    steps:
      - uses: actions/checkout@v4

      - name: Resolve tag and registry
        id: meta
        run: |
          if [ -n "${{ vars.REGISTRY }}" ]; then
            echo "REGISTRY=${{ vars.REGISTRY }}" >> "$GITHUB_ENV"
          fi
          if [ -n "${{ inputs.image_tag }}" ]; then
            echo "tag=${{ inputs.image_tag }}" >> "$GITHUB_OUTPUT"
          elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then
            echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
          else
            echo "image_tag input is required for workflow_dispatch without a tag" >&2
            exit 1
          fi

      - name: Log in to the Gitea registry
        run: |
          echo "${{ secrets.REGISTRY_TOKEN }}" \
            | docker login "${REGISTRY%%/*}" \
              -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin

      - name: Build and push clinical-api
        # Context MUST be repo root — ClinicalContracts is a sibling ProjectReference.
        run: |
          docker build -f VigilCareClinicalAPI/Dockerfile \
            -t "${REGISTRY}/clinical-api:${{ steps.meta.outputs.tag }}" \
            -t "${REGISTRY}/clinical-api:latest" .
          docker push "${REGISTRY}/clinical-api:${{ steps.meta.outputs.tag }}"
          docker push "${REGISTRY}/clinical-api:latest"

      - name: Build and push ward-gateway
        # Same repo-root context as docker-compose.yml's ward-gateway-api service.
        run: |
          docker build -f VigilCare.WardGateway/Dockerfile \
            -t "${REGISTRY}/ward-gateway:${{ steps.meta.outputs.tag }}" \
            -t "${REGISTRY}/ward-gateway:latest" .
          docker push "${REGISTRY}/ward-gateway:${{ steps.meta.outputs.tag }}"
          docker push "${REGISTRY}/ward-gateway:latest"

      - name: Build and push dashboard
        # Context is vigilcare-dashboard/ — package.json and nginx.conf live there.
        run: |
          docker build -f vigilcare-dashboard/Dockerfile \
            --build-arg VITE_API_URL="${{ vars.PROD_API_URL }}" \
            -t "${REGISTRY}/dashboard:${{ steps.meta.outputs.tag }}" \
            -t "${REGISTRY}/dashboard:latest" vigilcare-dashboard
          docker push "${REGISTRY}/dashboard:${{ steps.meta.outputs.tag }}"
          docker push "${REGISTRY}/dashboard:latest"

  migrate:
    needs: build-and-push
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/dotnet/sdk:8.0
    steps:
      - uses: actions/checkout@v4

      - name: Build migration bundle
        run: |
          dotnet tool install --global dotnet-ef --version 8.0.4 \
            || dotnet tool update --global dotnet-ef --version 8.0.4
          export PATH="$PATH:/root/.dotnet/tools"
          bash ./scripts/build-api-migration-bundle.sh
          cp -f ./artifacts/migrate-api ./migrate-api
          chmod +x ./migrate-api

      # Runs while the previous release is still serving traffic, so every
      # migration must be backwards-compatible with the outgoing image.
      # See Step 6 — expand-then-contract.
      - name: Apply migrations
        run: ./migrate-api --connection "${{ secrets.PG_CONNECTION_DDL }}"

  deploy:
    needs: [build-and-push, migrate]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H "${{ secrets.DEPLOY_HOST }}" >> ~/.ssh/known_hosts

      - name: Copy compose file
        run: |
          scp -i ~/.ssh/id_ed25519 docker-compose.prod.yml \
            "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:/opt/vigilcare/docker-compose.prod.yml"

      - name: Deploy
        env:
          IMAGE_TAG: ${{ needs.build-and-push.outputs.tag }}
        run: |
          ssh -i ~/.ssh/id_ed25519 \
            "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" \
            IMAGE_TAG="$IMAGE_TAG" bash -euo pipefail <<'EOF'
            cd /opt/vigilcare

            # Record the currently deployed tag so a rollback has a target.
            grep '^IMAGE_TAG=' .env > .env.previous || true

            if grep -q '^IMAGE_TAG=' .env; then
              sed -i "s|^IMAGE_TAG=.*|IMAGE_TAG=${IMAGE_TAG}|" .env
            else
              echo "IMAGE_TAG=${IMAGE_TAG}" >> .env
            fi

            docker compose -f docker-compose.prod.yml --env-file .env pull
            docker compose -f docker-compose.prod.yml --env-file .env up -d --remove-orphans
            docker image prune -f
          EOF

      - name: Smoke test
        run: |
          ssh -i ~/.ssh/id_ed25519 \
            "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" bash -euo pipefail <<'EOF'
            for i in $(seq 1 30); do
              if curl -fsS http://localhost:5270/health/ready >/dev/null; then
                echo "Ready check passed."
                curl -fsS http://localhost:5081/health/live >/dev/null && echo "Gateway live."
                curl -fsS http://localhost:8080/ >/dev/null && echo "Dashboard serving."
                exit 0
              fi
              sleep 5
            done
            echo "Ready check never passed — dumping API logs:"
            docker compose -f /opt/vigilcare/docker-compose.prod.yml --env-file /opt/vigilcare/.env logs --tail 100 api
            exit 1
          EOF

      - name: Roll back on failure
        if: failure()
        run: |
          ssh -i ~/.ssh/id_ed25519 \
            "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" bash -euo pipefail <<'EOF'
            cd /opt/vigilcare
            # Restores the previous image tag only. Schema changes are NOT
            # reverted — this is why migrations must be backwards-compatible.
            if [ -f .env.previous ]; then
              PREV=$(cut -d= -f2 .env.previous)
              sed -i "s|^IMAGE_TAG=.*|IMAGE_TAG=${PREV}|" .env
              docker compose -f docker-compose.prod.yml --env-file .env up -d
              echo "Rolled back to ${PREV}"
            fi
          EOF
