#!/usr/bin/env bash
# Copyright (c) Edgeless Systems GmbH
#
# SPDX-License-Identifier: AGPL-3.0-only

set -euo pipefail
shopt -s inherit_errexit

if [[ -z ${CONFIG_FILE-} ]] && [[ -f ${CONFIG_FILE-} ]]; then
  # shellcheck source=/dev/null
  . "${CONFIG_FILE}"
fi

CONTAINERS_JSON=$(mktemp /tmp/containers-XXXXXXXXXXXXXX.json)
declare -A AMI_FOR_REGION

import_status() {
  local import_task_id=$1
  aws ec2 describe-import-snapshot-tasks --region "${AWS_REGION}" --import-task-ids "${import_task_id}" | jq -r '.ImportSnapshotTasks[0].SnapshotTaskDetail.Status'
}

wait_for_import() {
  local import_task_id=$1
  local status
  echo -n "Waiting for import to finish"
  while true; do
    local status
    status=$(import_status "${import_task_id}")
    case "${status}" in
    completed)
      echo -e "\nImport completed."
      break
      ;;
    active)
      echo -n "."
      sleep 5
      ;;
    *)
      echo "Unexpected status: ${status}"
      exit 1
      ;;
    esac
  done
}

wait_for_image_available() {
  local ami_id=$1
  local region=$2
  echo -n "Waiting for image ${ami_id} to be available"
  while true; do
    # Waiter ImageAvailable failed: Max attempts exceeded
    local status
    status=$(aws ec2 wait image-available \
      --region "${region}" \
      --image-ids "${ami_id}" 2>&1 || true)
    case "${status}" in
    "")
      echo -e "\nImage available."
      break
      ;;
    *"Max attempts exceeded"*)
      echo -n "."
      ;;
    *)
      echo "Unexpected status: ${status}"
      exit 1
      ;;
    esac
  done
}

tag_ami_with_backing_snapshot() {
  local ami_id=$1
  local region=$2
  wait_for_image_available "${ami_id}" "${region}"
  local snapshot_id
  snapshot_id=$(aws ec2 describe-images \
    --region "${region}" \
    --image-ids "${ami_id}" \
    --output text --query "Images[0].BlockDeviceMappings[0].Ebs.SnapshotId")
  aws ec2 create-tags \
    --region "${region}" \
    --resources "${ami_id}" "${snapshot_id}" \
    --tags "Key=Name,Value=${AWS_IMAGE_NAME}"
}

make_ami_public() {
  local ami_id=$1
  local region=$2
  if [[ ${AWS_PUBLISH-} != "true" ]]; then
    return
  fi
  aws ec2 modify-image-attribute \
    --region "${region}" \
    --image-id "${ami_id}" \
    --launch-permission "Add=[{Group=all}]"
}

create_ami_from_raw_disk() {
  echo "Uploading raw disk image to S3"
  aws s3 cp "${AWS_IMAGE_PATH}" "s3://${AWS_BUCKET}/${AWS_IMAGE_FILENAME}" --no-progress
  printf '{
        "Description": "%s",
        "Format": "raw",
        "UserBucket": {
            "S3Bucket": "%s",
            "S3Key": "%s"
        }
    }' "${AWS_IMAGE_NAME}" "${AWS_BUCKET}" "${AWS_IMAGE_FILENAME}" > "${CONTAINERS_JSON}"
  IMPORT_SNAPSHOT=$(aws ec2 import-snapshot --region "${AWS_REGION}" --disk-container "file://${CONTAINERS_JSON}")
  echo "${IMPORT_SNAPSHOT}"
  IMPORT_TASK_ID=$(echo "${IMPORT_SNAPSHOT}" | jq -r '.ImportTaskId')
  aws ec2 describe-import-snapshot-tasks --region "${AWS_REGION}" --import-task-ids "${IMPORT_TASK_ID}"
  wait_for_import "${IMPORT_TASK_ID}"
  AWS_SNAPSHOT=$(aws ec2 describe-import-snapshot-tasks --region "${AWS_REGION}" --import-task-ids "${IMPORT_TASK_ID}" | jq -r '.ImportSnapshotTasks[0].SnapshotTaskDetail.SnapshotId')
  echo "Deleting raw disk image from S3"
  aws s3 rm "s3://${AWS_BUCKET}/${AWS_IMAGE_FILENAME}"
  rm "${CONTAINERS_JSON}"
  REGISTER_OUT=$(
    aws ec2 register-image \
      --region "${AWS_REGION}" \
      --name "${AWS_IMAGE_NAME}" \
      --boot-mode uefi \
      --architecture x86_64 \
      --root-device-name /dev/xvda \
      --block-device-mappings "DeviceName=/dev/xvda,Ebs={SnapshotId=${AWS_SNAPSHOT}}" \
      --ena-support \
      --tpm-support v2.0 \
      --uefi-data "$(cat "${AWS_EFIVARS_PATH}")"
  )
  IMAGE_ID=$(echo "${REGISTER_OUT}" | jq -r '.ImageId')
  AMI_FOR_REGION=(["${AWS_REGION}"]="${IMAGE_ID}")
  tag_ami_with_backing_snapshot "${IMAGE_ID}" "${AWS_REGION}"
  make_ami_public "${IMAGE_ID}" "${AWS_REGION}"
  echo "Imported initial AMI as ${IMAGE_ID} in ${AWS_REGION}"
}

replicate_ami() {
  local target_region=$1
  local replicated_image_out
  replicated_image_out=$(aws ec2 copy-image \
    --name "${AWS_IMAGE_NAME}" \
    --source-region "${AWS_REGION}" \
    --source-image-id "${IMAGE_ID}" \
    --region "${target_region}")
  local replicated_image_id
  replicated_image_id=$(echo "${replicated_image_out}" | jq -r '.ImageId')
  AMI_FOR_REGION["${target_region}"]=${replicated_image_id}
  echo "Replicated AMI as ${replicated_image_id} in ${target_region}"
}

create_ami_from_raw_disk
# replicate in parallel
for region in ${AWS_REPLICATION_REGIONS}; do
  replicate_ami "${region}"
done
# wait for all images to be available and tag + publish them
for region in ${AWS_REPLICATION_REGIONS}; do
  tag_ami_with_backing_snapshot "${AMI_FOR_REGION[${region}]}" "${region}"
  make_ami_public "${AMI_FOR_REGION[${region}]}" "${region}"
done

json=$(jq -ncS \
  --arg region "${AWS_REGION}" \
  --arg ami "${AMI_FOR_REGION[${AWS_REGION}]}" \
  '{"aws":{($region): $ami}}')
for region in ${AWS_REPLICATION_REGIONS}; do
  json=$(jq -ncS \
    --argjson json "${json}" \
    --arg region "${region}" \
    --arg ami "${AMI_FOR_REGION[${region}]}" \
    '$json * {"aws": {($region): $ami}}')
done

echo "${json}" > "${AWS_JSON_OUTPUT}"
