name: Docker - Update latest tag

on:
  workflow_dispatch:
    inputs:
      tag:
        description: 'The Docker image tag to pull'
        required: true
        type: string

jobs:
  retag-and-push:
    strategy:
      matrix:
        service:
          - frontend
          - backend
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3
        id: buildx
        with:
          install: true

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
        with:
          platforms: linux/amd64,linux/arm64

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_HUB_USER }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Get source image manifest and SHAs
        id: source-manifest
        run: |
          set -e
          echo "Fetching source manifest..."
          MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:${{ github.event.inputs.tag }})
          if [ -z "$MANIFEST" ]; then
            echo "No manifest found. Assuming single-arch image."
            exit 1
          fi
          
          echo "Original source manifest:"
          echo "$MANIFEST" | jq .
          
          AMD64_SHA=$(echo "$MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest')
          ARM64_SHA=$(echo "$MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="arm64" and .platform.os=="linux") | .digest')
          
          if [ -z "$AMD64_SHA" ] || [ -z "$ARM64_SHA" ]; then
            echo "Source image is not multi-arch (missing amd64 or arm64)"
            exit 1
          fi
          
          echo "Source amd64 manifest digest: $AMD64_SHA"
          echo "Source arm64 manifest digest: $ARM64_SHA"
          
          echo "amd64_sha=$AMD64_SHA" >> $GITHUB_OUTPUT
          echo "arm64_sha=$ARM64_SHA" >> $GITHUB_OUTPUT

      - name: Pull and retag architecture-specific images
        run: |
          set -e
          
          docker buildx inspect --bootstrap
          
          # Remove any existing local images to avoid cache interference
          echo "Removing existing local images if they exist..."
          docker image rm ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:${{ github.event.inputs.tag }} || true
          
          # Pull amd64 image by digest
          echo "Pulling amd64 image by digest..."
          docker pull --platform linux/amd64 ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }}
          PULLED_AMD64_MANIFEST_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
          PULLED_AMD64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} --format '{{.Id}}')
          echo "Pulled amd64 manifest digest: $PULLED_AMD64_MANIFEST_DIGEST"
          echo "Pulled amd64 image ID (sha256): $PULLED_AMD64_IMAGE_ID"
          
          # Pull arm64 image by digest
          echo "Pulling arm64 image by digest..."
          docker pull --platform linux/arm64 ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }}
          PULLED_ARM64_MANIFEST_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }} --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
          PULLED_ARM64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }} --format '{{.Id}}')
          echo "Pulled arm64 manifest digest: $PULLED_ARM64_MANIFEST_DIGEST"
          echo "Pulled arm64 image ID (sha256): $PULLED_ARM64_IMAGE_ID"
          
          # Tag the images
          docker tag ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64
          docker tag ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }} ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64
          
          # Verify tagged images
          TAGGED_AMD64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64 --format '{{.Id}}')
          TAGGED_ARM64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64 --format '{{.Id}}')
          echo "Tagged amd64 image ID (sha256): $TAGGED_AMD64_IMAGE_ID"
          echo "Tagged arm64 image ID (sha256): $TAGGED_ARM64_IMAGE_ID"

      - name: Push architecture-specific images
        run: |
          set -e
          
          echo "Pushing amd64 image..."
          docker push ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64
          PUSHED_AMD64_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64 --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
          echo "Pushed amd64 manifest digest (local): $PUSHED_AMD64_DIGEST"
          
          # Fetch manifest from registry after push
          echo "Fetching pushed amd64 manifest from registry..."
          PUSHED_AMD64_REGISTRY_MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64)
          PUSHED_AMD64_REGISTRY_DIGEST=$(echo "$PUSHED_AMD64_REGISTRY_MANIFEST" | jq -r '.config.digest')
          echo "Pushed amd64 manifest digest (registry): $PUSHED_AMD64_REGISTRY_DIGEST"
          
          echo "Pushing arm64 image..."
          docker push ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64
          PUSHED_ARM64_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64 --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
          echo "Pushed arm64 manifest digest (local): $PUSHED_ARM64_DIGEST"
          
          # Fetch manifest from registry after push
          echo "Fetching pushed arm64 manifest from registry..."
          PUSHED_ARM64_REGISTRY_MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64)
          PUSHED_ARM64_REGISTRY_DIGEST=$(echo "$PUSHED_ARM64_REGISTRY_MANIFEST" | jq -r '.config.digest')
          echo "Pushed arm64 manifest digest (registry): $PUSHED_ARM64_REGISTRY_DIGEST"

      - name: Create and push multi-arch manifest with original digests
        run: |
          set -e
          
          echo "Creating multi-arch manifest with original digests..."
          docker manifest create ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest \
            ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} \
            ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }}
            
          echo "Pushing multi-arch manifest..."
          docker manifest push ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest

      - name: Clean up intermediate tags
        if: success()
        run: |
          docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64 || true
          docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64 || true
          docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:${{ github.event.inputs.tag }} || true

      - name: Verify final manifest
        run: |
          set -e
          echo "Fetching final generated manifest..."
          FINAL_MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest)
          echo "Generated final manifest:"
          echo "$FINAL_MANIFEST" | jq .
          
          FINAL_AMD64_SHA=$(echo "$FINAL_MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest')
          FINAL_ARM64_SHA=$(echo "$FINAL_MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="arm64" and .platform.os=="linux") | .digest')
          
          echo "Final amd64 manifest digest: $FINAL_AMD64_SHA"
          echo "Final arm64 manifest digest: $FINAL_ARM64_SHA"
          
          # Compare all digests
          echo "Comparing digests..."
          echo "Source amd64 digest: ${{ steps.source-manifest.outputs.amd64_sha }}"
          echo "Pulled amd64 manifest digest: $PULLED_AMD64_MANIFEST_DIGEST"
          echo "Pushed amd64 manifest digest (local): $PUSHED_AMD64_DIGEST"
          echo "Pushed amd64 manifest digest (registry): $PUSHED_AMD64_REGISTRY_DIGEST"
          echo "Final amd64 digest: $FINAL_AMD64_SHA"
          echo "Source arm64 digest: ${{ steps.source-manifest.outputs.arm64_sha }}"
          echo "Pulled arm64 manifest digest: $PULLED_ARM64_MANIFEST_DIGEST"
          echo "Pushed arm64 manifest digest (local): $PUSHED_ARM64_DIGEST"
          echo "Pushed arm64 manifest digest (registry): $PUSHED_ARM64_REGISTRY_DIGEST"
          echo "Final arm64 digest: $FINAL_ARM64_SHA"
          
          if [ "$FINAL_AMD64_SHA" != "${{ steps.source-manifest.outputs.amd64_sha }}" ] || [ "$FINAL_ARM64_SHA" != "${{ steps.source-manifest.outputs.arm64_sha }}" ]; then
            echo "Error: Final manifest SHAs do not match source SHAs"
            exit 1
          fi
          
          echo "Successfully created multi-arch ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest from ${{ github.event.inputs.tag }}"
