# Reference: https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners

# Conditions for pushing the image to GHCR:
#   - Triggered by push to default branch (`main`)
#   - Triggered by push to a version tag (`v*`)
#   - Triggered by a scheduled run
#   - Triggered manually via `workflow_dispatch` with `push: true`
#
# Conditional expression:
#   github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'schedule' || github.event.inputs.push

name: Publish Docker image

on:
  workflow_dispatch:
    inputs:
      ref:
        description: 'Git ref (tag or commit SHA) to build'
        required: true
        type: string 
        default: 'main'
      push:
        description: 'Push the image to container registry'
        required: false
        type: boolean
        default: false
  push:
    tags:
      - 'v*'
    branches:
      - main
    paths-ignore:
      - 'charts/**'
  schedule:
    - cron: '30 1 * * *'

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

permissions:
  contents: write
  packages: write

jobs:
  ci:
    uses: ./.github/workflows/ci.yml

  build:
    name: Build Docker image
    needs: [ ci ]

    strategy:
      fail-fast: false
      matrix:
        platform: [amd64, arm64]

    timeout-minutes: 60
    runs-on: ${{ matrix.platform == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}

    outputs:
      tags: ${{ steps.meta.outputs.tags }}

    permissions:
      contents: read
      packages: write

    steps:
      - name: Check out the repo
        uses: actions/checkout@v4.2.0
        with:
          ref: ${{ github.event.inputs.ref || github.ref }}

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3.10.0

      - name: Log in to the container registry
        uses: docker/login-action@v3.3.0
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Configure image tags
        id: tag_config
        shell: bash
        run: |
          BASE_CONFIG="type=sha,format=long"
          if [[ $GITHUB_EVENT_NAME == "schedule" ]]; then
            BASE_CONFIG+=$'\n'"type=schedule,pattern=nightly"
            BASE_CONFIG+=$'\n'"type=schedule,pattern=nightly-{{date 'ddd'}}"
          elif [[ "$GITHUB_REF" == refs/tags/* ]]; then
            TAG_NAME="${GITHUB_REF#refs/tags/}"
            if [[ "$TAG_NAME" == v* ]]; then
              BASE_CONFIG="type=semver,pattern={{version}}"
              if [[ "$TAG_NAME" == v*-alpha* ]]; then
                BASE_CONFIG+=$'\n'"type=raw,value=latest"
              else
                BASE_CONFIG+=$'\n'"type=raw,value=stable"
                BASE_CONFIG+=$'\n'"type=raw,value=latest"
              fi
            fi
          fi
          {
            echo 'TAGS_SPEC<<EOF'
            echo "$BASE_CONFIG"
            echo EOF
          } >> $GITHUB_ENV

      - name: Get current date (RFC 3339 format)
        id: date
        run: echo "date=$(date -Iseconds)" >> $GITHUB_OUTPUT

      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@v5.6.0
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          flavor: latest=false
          tags: ${{ env.TAGS_SPEC }}
          labels: |
            org.opencontainers.image.version=${{ startsWith(github.ref, 'refs/tags/v') && github.ref_name || '' }}
            org.opencontainers.image.created=${{ steps.date.outputs.date }}
            org.opencontainers.image.ref.name=${{ github.ref_name }}
            org.opencontainers.image.vendor=we-promise
            org.opencontainers.image.title=Sure
            org.opencontainers.image.description=A multi-arch Docker image for the Sure Rails app

      - name: Publish 'linux/${{ matrix.platform }}' image by digest
        uses: docker/build-push-action@v6.16.0
        id: build
        with:
          context: .
          build-args: BUILD_COMMIT_SHA=${{ github.sha }}
          platforms: 'linux/${{ matrix.platform }}'
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:cache-${{ matrix.platform }}
          cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:cache-${{ matrix.platform }},mode=max
          labels: ${{ steps.meta.outputs.labels }}
          provenance: false
          push: true
          # DO NOT REMOVE `oci-mediatypes=true`, fixes annotation not showing up on job.merge.steps[-1]
          # ref: https://github.com/docker/build-push-action/discussions/1022
          outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},name-canonical=true,push-by-digest=true,oci-mediatypes=true

      - name: Export the Docker image digest
        if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'schedule' || github.event.inputs.push }}
        run: |
          mkdir -p "${RUNNER_TEMP}"/digests
          echo "${DIGEST#sha256:}" > "${RUNNER_TEMP}/digests/digest-${PLATFORM}"
        env:
          DIGEST: ${{ steps.build.outputs.digest }}
          PLATFORM: ${{ matrix.platform }}

      - name: Upload the Docker image digest
        if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'schedule' || github.event.inputs.push }}
        uses: actions/upload-artifact@v4.6.2
        with:
          name: digest-${{ matrix.platform }}
          path: ${{ runner.temp }}/digests/*
          if-no-files-found: error
          retention-days: 1

  merge:
    name: Merge multi-arch manifest & push multi-arch tag
    if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'schedule' || github.event.inputs.push }}
    needs: [build]

    timeout-minutes: 60
    runs-on: 'ubuntu-24.04'

    permissions:
      packages: write

    steps:
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3.10.0

      - name: Download Docker image digests
        uses: actions/download-artifact@v4.3.0
        with:
          path: ${{ runner.temp }}/digests
          pattern: digest-*
          merge-multiple: true

      - name: Log in to the container registry
        uses: docker/login-action@v3.3.0
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Merge and push Docker image
        env:
          TAGS: ${{ needs.build.outputs.tags }}
          DIGESTS_DIR: ${{ runner.temp }}/digests
        shell: bash -xeuo pipefail {0}
        run: |
          tag_args=()
          while IFS=$'\n' read -r tag; do
            [[ -n "${tag}" ]] || continue
            tag_args+=("--tag=${tag}")
          done <<< "${TAGS}"

          image_args=()
          for PLATFORM in amd64 arm64; do
            image_args+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:$(<"${DIGESTS_DIR}/digest-${PLATFORM}")")
          done

          annotations=(
            "index:org.opencontainers.image.created=$(date -Iseconds)"
            'index:org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}'
            'index:org.opencontainers.image.revision=${{ github.sha }}'
            'index:org.opencontainers.image.ref.name=${{ github.ref_name }}'
            'index:org.opencontainers.image.vendor=we-promise'
            'index:org.opencontainers.image.licenses=AGPL-3.0'
            'index:org.opencontainers.image.title=Sure'
            'index:org.opencontainers.image.description=A multi-arch Docker image for the Sure Rails app'
          )
          annotation_args=()
          for annotation in "${annotations[@]}"; do
            annotation_args+=("--annotation=${annotation}")
          done
          if [[ $GITHUB_REF_TYPE == "tag" ]]; then
            annotation_args+=("--annotation=index:org.opencontainers.image.version=$GITHUB_REF_NAME")
          fi

          attempts=0
          until docker buildx imagetools create \
            "${annotation_args[@]}" \
            "${tag_args[@]}" \
            "${image_args[@]}" \
          ; do
            attempts=$((attempts + 1))
            if [[ $attempts -ge 3 ]]; then
              echo "[$(date -u)] ERROR: Failed after 3 attempts." >&2
              exit 1
            fi
            delay=$((2 ** attempts))
            if [[ $delay -gt 15 ]]; then delay=15; fi
            echo "Push failed (attempt $attempts). Retrying in ${delay} seconds..."
            sleep ${delay}
          done

  mobile:
    name: Build Mobile Apps
    if: startsWith(github.ref, 'refs/tags/v')
    uses: ./.github/workflows/flutter-build.yml
    secrets: inherit

  release:
    name: Create GitHub Release
    if: startsWith(github.ref, 'refs/tags/v')
    needs: [merge, mobile]
    runs-on: ubuntu-latest
    timeout-minutes: 10

    permissions:
      contents: write

    steps:
      - name: Download Android APK artifact
        uses: actions/download-artifact@v4.3.0
        with:
          name: app-release-apk
          path: ${{ runner.temp }}/mobile-artifacts

      - name: Download iOS build artifact
        uses: actions/download-artifact@v4.3.0
        with:
          name: ios-build-unsigned
          path: ${{ runner.temp }}/ios-build

      - name: Prepare release assets
        run: |
          mkdir -p ${{ runner.temp }}/release-assets

          echo "=== Debugging: List downloaded artifacts ==="
          echo "Mobile artifacts:"
          ls -laR "${{ runner.temp }}/mobile-artifacts" || echo "No mobile-artifacts directory"
          echo "iOS build:"
          ls -laR "${{ runner.temp }}/ios-build" || echo "No ios-build directory"
          echo "==========================================="

          # Copy debug APK if it exists
          if [ -f "${{ runner.temp }}/mobile-artifacts/app-debug.apk" ]; then
            cp "${{ runner.temp }}/mobile-artifacts/app-debug.apk" "${{ runner.temp }}/release-assets/sure-${{ github.ref_name }}-debug.apk"
            echo "✓ Debug APK prepared"
          fi

          # Copy release APK if it exists
          if [ -f "${{ runner.temp }}/mobile-artifacts/app-release.apk" ]; then
            cp "${{ runner.temp }}/mobile-artifacts/app-release.apk" "${{ runner.temp }}/release-assets/sure-${{ github.ref_name }}.apk"
            echo "✓ Release APK prepared"
          fi

          # Create iOS app archive (zip the .app bundle)
          # Path preserves directory structure from artifact upload
          if [ -d "${{ runner.temp }}/ios-build/ios/iphoneos/Runner.app" ]; then
            cd "${{ runner.temp }}/ios-build/ios/iphoneos"
            zip -r "${{ runner.temp }}/release-assets/sure-${{ github.ref_name }}-ios-unsigned.zip" Runner.app
            echo "✓ iOS build archive prepared"
          fi

          # Copy iOS build info
          if [ -f "${{ runner.temp }}/ios-build/ios-build-info.txt" ]; then
            cp "${{ runner.temp }}/ios-build/ios-build-info.txt" "${{ runner.temp }}/release-assets/"
          fi

          echo "Release assets:"
          ls -la "${{ runner.temp }}/release-assets/"

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ github.ref_name }}
          name: ${{ github.ref_name }}
          draft: false
          prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'rc') }}
          generate_release_notes: true
          files: |
            ${{ runner.temp }}/release-assets/*
          body: |
            ## Mobile Debug Builds

            This release includes debug builds of the mobile applications:

            - **Android APK**: Debug build for testing on Android devices
            - **iOS Build**: Unsigned iOS build (requires code signing for installation)

            > **Note**: These are debug builds intended for testing purposes. For production use, please build from source with proper signing credentials.

  bump-alpha-version:
    name: Bump Alpha Version
    if: startsWith(github.ref, 'refs/tags/v') && contains(github.ref_name, 'alpha')
    needs: [merge]
    runs-on: ubuntu-latest
    timeout-minutes: 10

    permissions:
      contents: write

    steps:
      - name: Check out main branch
        uses: actions/checkout@v4.2.0
        with:
          ref: main
          token: ${{ secrets.GH_PAT }}

      - name: Bump alpha version
        run: |
           VERSION_FILE="config/initializers/version.rb"

           # Ensure version file exists
           if [ ! -f "$VERSION_FILE" ]; then
             echo "ERROR: Version file not found: $VERSION_FILE"
             exit 1
           fi
 
           # Extract current version
           CURRENT_VERSION=$(grep -oP '"\K[0-9]+\.[0-9]+\.[0-9]+-alpha\.[0-9]+' "$VERSION_FILE")
           if [ -z "$CURRENT_VERSION" ]; then
             echo "ERROR: Could not extract version from $VERSION_FILE"
             exit 1
           fi
           echo "Current version: $CURRENT_VERSION"
 
           # Extract the alpha number and increment it
           ALPHA_NUM=$(echo "$CURRENT_VERSION" | grep -oP 'alpha\.\K[0-9]+')
           if [ -z "$ALPHA_NUM" ]; then
             echo "ERROR: Could not extract alpha number from $CURRENT_VERSION"
             exit 1
           fi
           NEW_ALPHA_NUM=$((ALPHA_NUM + 1))
 
           # Create new version string
           BASE_VERSION=$(echo "$CURRENT_VERSION" | grep -oP '^[0-9]+\.[0-9]+\.[0-9]+')
           if [ -z "$BASE_VERSION" ]; then
             echo "ERROR: Could not extract base version from $CURRENT_VERSION"
             exit 1
           fi
           NEW_VERSION="${BASE_VERSION}-alpha.${NEW_ALPHA_NUM}"
           echo "New version: $NEW_VERSION"

           # Update the version file
           sed -i "s/\"$CURRENT_VERSION\"/\"$NEW_VERSION\"/" "$VERSION_FILE"

           # Verify the change
           echo "Updated version.rb:"
           grep "semver" "$VERSION_FILE"

      - name: Commit and push version bump
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

          git add config/initializers/version.rb

          # Check if there are changes to commit
          if git diff --cached --quiet; then
            echo "No changes to commit - version may have already been bumped"
            exit 0
          fi

          git commit -m "Bump version to next alpha after ${{ github.ref_name }} release"

          # Push with retry logic
          attempts=0
          until git push origin main; do
            attempts=$((attempts + 1))
            if [[ $attempts -ge 4 ]]; then
              echo "ERROR: Failed to push after 4 attempts." >&2
              exit 1
            fi
            delay=$((2 ** attempts))
            echo "Push failed (attempt $attempts). Retrying in ${delay} seconds..."
            sleep ${delay}
            git pull --rebase origin main
          done
