name: Deploy To Prod (ECR -> Elastic Beanstalk with Versioning)

on:
  push:
    branches: [main]
    paths-ignore:
      - "CHANGELOG.md"
  workflow_dispatch:

concurrency:
  group: eb-backend-prod-{% raw %}${{ github.ref }}{% endraw %}

  cancel-in-progress: false

env:
  AWS_REGION: {% raw %}${{ secrets.PROD_AWS_REGION }}{% endraw %}

  ACCOUNT_ID: {% raw %}${{ secrets.AWS_ACCOUNT_ID }}{% endraw %}

  ECR_REPO: {{ project_name }}
  EB_APP: {{ project_name }}
  S3_BUCKET: elasticbeanstalk-{% raw %}${{ secrets.PROD_AWS_REGION }}{% endraw %}-{% raw %}${{ secrets.AWS_ACCOUNT_ID }}{% endraw %}

  PLATFORM_ENV: api-prod

permissions:
  contents: write
  id-token: write
  issues: write
  pull-requests: write

jobs:
  compute_version:
    name: Compute version from latest draft
    runs-on: ubuntu-latest
    outputs:
      tag: "{% raw %}${{ steps.version.outputs.tag }}{% endraw %}"
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - name: Get version from draft release or git tags
        id: version
        env:
          GH_TOKEN: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %}
        run: |
          # Try to find the draft release created by dev CD
          DRAFT_TAG=$(gh release list --limit 10 --json tagName,isDraft \
            --jq '.[] | select(.isDraft==true) | .tagName' | head -1)
          if [ -n "$DRAFT_TAG" ]; then
            echo "tag=${DRAFT_TAG}" >> "$GITHUB_OUTPUT"
            echo "Using draft release tag: ${DRAFT_TAG}"
          else
            # Fallback: compute semver from PR labels
            LATEST=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
            MAJOR=$(echo "$LATEST" | sed 's/^v//' | cut -d. -f1)
            MINOR=$(echo "$LATEST" | sed 's/^v//' | cut -d. -f2)
            PATCH=$(echo "$LATEST" | sed 's/^v//' | cut -d. -f3)

            SINCE=$(git log -1 --format=%aI "$LATEST" 2>/dev/null || echo "2000-01-01")
            LABELS=$(gh pr list --state merged --base main \
              --json mergedAt,labels \
              --jq "[.[] | select(.mergedAt >= \"$SINCE\") | .labels[].name] | unique | .[]" 2>/dev/null || echo "")

            BUMP="patch"
            if echo "$LABELS" | grep -q "breaking"; then
              BUMP="major"
            elif echo "$LABELS" | grep -q "feature"; then
              BUMP="minor"
            fi

            case "$BUMP" in
              major) NEXT="v$((MAJOR + 1)).0.0" ;;
              minor) NEXT="v${MAJOR}.$((MINOR + 1)).0" ;;
              patch) NEXT="v${MAJOR}.${MINOR}.$((PATCH + 1))" ;;
            esac

            echo "tag=${NEXT}" >> "$GITHUB_OUTPUT"
            echo "No draft found. Fallback: ${BUMP} bump ${LATEST} -> ${NEXT}"
          fi

  build_and_publish:
    name: Build image & create EB app version (once)
    runs-on: ubuntu-latest
    needs: [compute_version]
    outputs:
      version_label: {% raw %}${{ steps.meta.outputs.version_label }}{% endraw %}

      zip_key: {% raw %}${{ steps.meta.outputs.zip_key }}{% endraw %}

    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: {% raw %}${{ env.AWS_REGION }}{% endraw %}

          role-to-assume: arn:aws:iam::{% raw %}${{ secrets.AWS_ACCOUNT_ID }}{% endraw %}:role/GHA-EB-Deploy
          role-session-name: gha-eb-api

      - name: Install tools
        run: sudo apt-get update && sudo apt-get install -y jq zip

      - name: Login to ECR
        id: ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Set image tags and version label
        id: meta
        run: |
          REG="{% raw %}${{ steps.ecr.outputs.registry }}{% endraw %}"
          IMAGE="${REG}/{% raw %}${{ env.ECR_REPO }}{% endraw %}"
          TAG="{% raw %}${{ env.PLATFORM_ENV }}{% endraw %}-{% raw %}${{ needs.compute_version.outputs.tag }}{% endraw %}"
          VERSION_LABEL="${TAG}-build-${GITHUB_RUN_NUMBER}"
          ZIP_KEY="{{ project_name }}-${VERSION_LABEL}.zip"
          echo "image_primary=${IMAGE}:${VERSION_LABEL}" >> $GITHUB_OUTPUT
          echo "version_label=${VERSION_LABEL}" >> $GITHUB_OUTPUT
          echo "zip_key=${ZIP_KEY}" >> $GITHUB_OUTPUT
          echo "Using VERSION_LABEL=${VERSION_LABEL}"

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container
          use: true

      - name: Build & push image to ECR
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: |
            {% raw %}${{ steps.meta.outputs.image_primary }}{% endraw %}

          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Render Dockerrun & bundle hooks
        run: |
          set -e
          IMAGE="{% raw %}${{ steps.meta.outputs.image_primary }}{% endraw %}"
          BUNDLE_DIR="$(mktemp -d)"
          OUT_ZIP="{% raw %}${{ github.workspace }}{% endraw %}/{% raw %}${{ steps.meta.outputs.zip_key }}{% endraw %}"
          # Render Dockerrun
          sed "s|\${IMAGE}|$IMAGE|g" infra/Dockerrun.aws.json.tmpl > "${BUNDLE_DIR}/Dockerrun.aws.json"
          jq . "${BUNDLE_DIR}/Dockerrun.aws.json" > "${BUNDLE_DIR}/Dockerrun.clean.json" && mv "${BUNDLE_DIR}/Dockerrun.clean.json" "${BUNDLE_DIR}/Dockerrun.aws.json"
          # Include hooks/configs
          if [ -d ".platform" ]; then
            cp -a .platform "${BUNDLE_DIR}/.platform"
            find "${BUNDLE_DIR}/.platform" -type f -name "*.sh" -exec chmod +x {} +
          fi
          if [ -d ".ebextensions" ]; then
            cp -a .ebextensions "${BUNDLE_DIR}/.ebextensions"
          fi
          ( cd "${BUNDLE_DIR}"; zip -r "$OUT_ZIP" Dockerrun.aws.json $( [ -d ".platform" ] && echo ".platform" ) $( [ -d ".ebextensions" ] && echo ".ebextensions" ) >/dev/null )
          echo "Bundle: $OUT_ZIP"

      - name: Upload bundle to S3
        run: |
          aws s3 cp "{% raw %}${{ steps.meta.outputs.zip_key }}{% endraw %}" \
            "s3://{% raw %}${{ env.S3_BUCKET }}{% endraw %}/{% raw %}${{ steps.meta.outputs.zip_key }}{% endraw %}" \
            --region "{% raw %}${{ env.AWS_REGION }}{% endraw %}"

      - name: Create EB application version (idempotent)
        run: |
          set -e
          LABEL="{% raw %}${{ steps.meta.outputs.version_label }}{% endraw %}"
          KEY="{% raw %}${{ steps.meta.outputs.zip_key }}{% endraw %}"
          EXISTS=$(aws elasticbeanstalk describe-application-versions \
            --region "{% raw %}${{ env.AWS_REGION }}{% endraw %}" \
            --application-name "{% raw %}${{ env.EB_APP }}{% endraw %}" \
            --version-labels "$LABEL" \
            --query 'length(ApplicationVersions)' --output text)
          if [[ "$EXISTS" -eq 0 ]]; then
            aws elasticbeanstalk create-application-version \
              --region "{% raw %}${{ env.AWS_REGION }}{% endraw %}" \
              --application-name "{% raw %}${{ env.EB_APP }}{% endraw %}" \
              --version-label "$LABEL" \
              --source-bundle S3Bucket={% raw %}${{ env.S3_BUCKET }}{% endraw %},S3Key="${KEY}"
          else
            echo "EB app version $LABEL already exists — skipping create."
          fi

  deploy_to_envs:
    name: Deploy to EB envs
    runs-on: ubuntu-latest
    needs: [build_and_publish]
    strategy:
      matrix:
        env_name: [api-prod, worker-prod]
    steps:
      - name: Configure AWS (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: {% raw %}${{ env.AWS_REGION }}{% endraw %}

          role-to-assume: arn:aws:iam::{% raw %}${{ secrets.AWS_ACCOUNT_ID }}{% endraw %}:role/GHA-EB-Deploy
          role-session-name: gha-eb-api

      - name: Update EB env if version differs
        run: |
          set -e
          ENV_NAME="{% raw %}${{ matrix.env_name }}{% endraw %}"
          TARGET="{% raw %}${{ needs.build_and_publish.outputs.version_label }}{% endraw %}"

          CURRENT=$(aws elasticbeanstalk describe-environments \
            --environment-names "$ENV_NAME" \
            --query 'Environments[0].VersionLabel' --output text)

          echo "Env: $ENV_NAME | Current=${CURRENT} | Target=${TARGET}"

          if [ "$CURRENT" = "$TARGET" ]; then
            echo "Version already deployed; skipping update."
            exit 0
          fi

          for attempt in 1 2 3; do
            set +e
            aws elasticbeanstalk update-environment \
              --environment-name "$ENV_NAME" \
              --version-label "$TARGET"
            RC=$?
            set -e
            if [ $RC -eq 0 ]; then
              echo "Update initiated."
              break
            fi
            echo "update-environment failed (attempt $attempt). Retrying in 10s..."
            sleep 10
          done
          if [ $RC -ne 0 ]; then
            echo "Failed to call update-environment after retries."
            exit 1
          fi

      - name: Wait for EB environment to be Ready
        timeout-minutes: 20
        run: |
          set -e
          ENV_NAME="{% raw %}${{ matrix.env_name }}{% endraw %}"
          TARGET="{% raw %}${{ needs.build_and_publish.outputs.version_label }}{% endraw %}"
          for i in {1..120}; do
            DESC=$(aws elasticbeanstalk describe-environments --environment-names "$ENV_NAME")
            STATUS=$(echo "$DESC" | jq -r '.Environments[0].Status')
            HEALTH=$(echo "$DESC" | jq -r '.Environments[0].Health')
            VERSION=$(echo "$DESC" | jq -r '.Environments[0].VersionLabel')
            echo "[$i] $ENV_NAME -> Status=$STATUS | Health=$HEALTH | Version=$VERSION (target=$TARGET)"
            if [ "$STATUS" = "Ready" ] && [ "$VERSION" = "$TARGET" ]; then
              echo "Environment is Ready on target version."
              exit 0
            fi
            sleep 10
          done
          echo "Timed out waiting for Ready status on $ENV_NAME"
          exit 1

      - name: Best-effort health summary
        continue-on-error: true
        run: |
          ENV_NAME="{% raw %}${{ matrix.env_name }}{% endraw %}"
          aws elasticbeanstalk describe-environments \
            --environment-names "$ENV_NAME" \
            --query 'Environments[0].[EnvironmentName,Health,HealthStatus,Status,EndpointURL,VersionLabel]' --output table

  publish_release:
    name: Publish release & update changelog
    runs-on: ubuntu-latest
    needs: [compute_version, deploy_to_envs]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %}

      - name: Publish draft release
        env:
          GH_TOKEN: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %}
        run: |
          TAG="{% raw %}${{ needs.compute_version.outputs.tag }}{% endraw %}"

          # Check if draft exists for this tag
          IS_DRAFT=$(gh release view "$TAG" --json isDraft --jq '.isDraft' 2>/dev/null || echo "")
          if [ "$IS_DRAFT" = "true" ]; then
            gh release edit "$TAG" --draft=false
            echo "Published release $TAG"
          else
            # No draft — create and publish directly
            gh release create "$TAG" \
              --generate-notes \
              --target main \
              --title "$TAG"
            echo "Created and published release $TAG"
          fi

      - name: Update CHANGELOG.md
        env:
          GH_TOKEN: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %}
        run: |
          TAG="{% raw %}${{ needs.compute_version.outputs.tag }}{% endraw %}"
          DATE=$(date +%Y-%m-%d)

          # Get release body
          BODY=$(gh release view "$TAG" --json body --jq '.body')

          # Build new entry
          ENTRY=$(printf "## %s (%s)\n\n%s\n" "$TAG" "$DATE" "$BODY")

          # Prepend to CHANGELOG.md after the header lines
          if [ -f CHANGELOG.md ]; then
            # Keep first 3 lines (header), insert entry, then rest
            HEAD=$(head -3 CHANGELOG.md)
            TAIL=$(tail -n +4 CHANGELOG.md)
            printf "%s\n\n%s\n%s\n" "$HEAD" "$ENTRY" "$TAIL" > CHANGELOG.md
          else
            printf "# Changelog\n\nAll notable changes to this project will be documented in this file.\nThis changelog is automatically updated on each production deployment.\n\n%s\n" "$ENTRY" > CHANGELOG.md
          fi

      - name: Update version in pyproject.toml
        run: |
          TAG="{% raw %}${{ needs.compute_version.outputs.tag }}{% endraw %}"
          VERSION="${TAG#v}"
          sed -i "s/^version = \".*\"/version = \"${VERSION}\"/" pyproject.toml

      - name: Commit release updates
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add CHANGELOG.md pyproject.toml
          git diff --cached --quiet && exit 0
          git commit -m "release: {% raw %}${{ needs.compute_version.outputs.tag }}{% endraw %} [skip ci]"
          git push
