#! /usr/bin/env bash

set -euo pipefail

function get_amd64_digest_by_ref() {
  img_ref=$1
  # The docker image is multi-arch, but the digests are per architecture. We support amd64 clusters only, so pick that digest.
  docker manifest inspect "$img_ref" | jq -r '.manifests[] | select(.platform.architecture=="amd64") | .digest'
}

script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "${script_dir}/lib/libcli.source"

if [ -z "${1:-}" ]; then
    _error "Usage: $0 <image-reference>"
else
    imageReference=$1
fi

[[ -z "${MAKE}" || ! -f 'target/docker.id' ]] && _error 'must be run from within from make'

developerVersionUsed=""
if [[ "$imageReference" == *-dirty ]]; then
    # We allow overwriting images only for dirty tags (developer versions).
    developerVersionUsed=1
fi

imageId="$(<target/docker.id)"

targetPushState="${imageId}::${imageReference}"

# Matching (imageID, imageReference) skips build, since the exact imageID has already been pushed
if [ -f "target/docker.push" ]; then
    prevPushState="$(<target/docker.push)"
    if [ "${targetPushState}" = "${prevPushState}" ]; then
      _log "Docker image with ID ${imageId} already tagged and pushed to ${imageReference}. Skipping push."
      exit 0
    fi
fi

found_docker_image_id=""
max_attempts=5

for ((i=1; i<=max_attempts; i++)); do
  if docker image ls -q --no-trunc --digests --all | grep -q "${imageId}"; then
    found_docker_image_id=1
    break
  fi
  _warning "Failed to find Docker image ID '${imageId}' in local images. Attempt ${i} of ${max_attempts}."
  sleep 1
done

if [ -z "$found_docker_image_id" ]; then
  _error "Docker image ID '${imageId}' could not be found (should have been built by 'docker-build') after ${max_attempts} attempts.\nCannot tag image for push of '${imageReference}'."
  exit 1
fi

if docker-image-reference-exists "${imageReference}"; then
  remoteImageId=$(get-docker-image-id "${imageReference}")
  if [ "${imageId}" = "${remoteImageId}" ]; then
    digest=$(get_amd64_digest_by_ref "$imageReference" || true)
    if [ -n "$digest" ]; then
      _log "Image with image ID '${imageId}' already exists in repo:\n${imageReference}\nSkipping push."
      exit 0
    else
      _error "Image with image ID '${imageId}' already exists in repo but does not have an amd64 manifest which indicates a partial failure:\n${imageReference}\nFailing push.\nPlease manually delete the images for this version."
      exit 1
    fi
  else
    if [ -z "${developerVersionUsed}" ]; then
      digest=$(get_amd64_digest_by_ref "$imageReference" || true)
      if [ -n "$digest" ]; then
        _info "Image already exists in repo with image ID '${remoteImageId}'\nThis does not match local image ID:         '${imageId}'\n${imageReference}\nNot pushing image."
        exit 0
      else
        _error "Image already exists in repo with image ID '${remoteImageId}'\nThis does not match local image ID:         '${imageId}'\n${imageReference}\nError: Cannot find amd64 manifest (partial failure), failing push.\nPlease manually delete the images for this version."
        exit 1
      fi
    fi
  fi
fi

_info "Tagging image ID ${imageId} with reference ${imageReference}"
if ! _set_x docker tag "${imageId}" "${imageReference}"; then
    _error "Could not tag image '${imageId}."
    exit 1
fi

pushed=""
max_attempts=5

_info "Pushing Docker image ${imageReference} (with ID ${imageId})"
for ((i=1; i<=max_attempts; i++)); do
  if _set_x docker push "${imageReference}"; then
    pushed=1
    break
  fi
  _warning "Failed to push Docker image '${imageReference}' with ID '${imageId}'. Attempt ${i} of ${max_attempts}."
  sleep 1
done

if [ -z "$pushed" ]; then
  _error "Failed to push Docker image '${imageReference}' with ID '${imageId}' after ${max_attempts} attempts."
  exit 1
fi

_info "Successfully pushed Docker image ${imageReference} (with ID ${imageId})"

echo "${targetPushState}" > target/docker.push
