#!/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

CREATE_SIG_VERSION=NO
POSITIONAL_ARGS=()

while [[ $# -gt 0 ]]; do
  case $1 in
  -g | --gallery)
    CREATE_SIG_VERSION=YES
    shift # past argument
    ;;
  --disk-name)
    AZURE_DISK_NAME="$2"
    shift # past argument
    shift # past value
    ;;
  -*)
    echo "Unknown option $1"
    exit 1
    ;;
  *)
    POSITIONAL_ARGS+=("$1") # save positional arg
    shift                   # past argument
    ;;
  esac
done

set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters

if [[ ${AZURE_SECURITY_TYPE} == "ConfidentialVM" ]]; then
  AZURE_DISK_SECURITY_TYPE=ConfidentialVM_VMGuestStateOnlyEncryptedWithPlatformKey
  AZURE_SIG_VERSION_ENCRYPTION_TYPE=EncryptedVMGuestStateOnlyWithPmk
  security_type_short_name="cvm"
elif [[ ${AZURE_SECURITY_TYPE} == "ConfidentialVMSupported" ]]; then
  AZURE_DISK_SECURITY_TYPE=""
  security_type_short_name="cvm"
elif [[ ${AZURE_SECURITY_TYPE} == "TrustedLaunch" ]]; then
  AZURE_DISK_SECURITY_TYPE=TrustedLaunch
  security_type_short_name="trustedlaunch"
else
  echo "Unknown security type: ${AZURE_SECURITY_TYPE}"
  exit 1
fi

AZURE_CVM_ENCRYPTION_ARGS=""
if [[ -n ${AZURE_SIG_VERSION_ENCRYPTION_TYPE-} ]]; then
  AZURE_CVM_ENCRYPTION_ARGS=" --target-region-cvm-encryption "
  for _ in ${AZURE_REPLICATION_REGIONS}; do
    AZURE_CVM_ENCRYPTION_ARGS=" ${AZURE_CVM_ENCRYPTION_ARGS} ${AZURE_SIG_VERSION_ENCRYPTION_TYPE}, "
  done
fi
echo "Replicating image in ${AZURE_REPLICATION_REGIONS}"

AZURE_VMGS_PATH=$1
if [[ -z ${AZURE_VMGS_PATH} ]] && [[ ${AZURE_SECURITY_TYPE} == "ConfidentialVM" ]]; then
  echo "No VMGS path provided - using default ConfidentialVM VMGS"
  AZURE_VMGS_PATH="${BLOBS_DIR}/cvm-vmgs.vhd"
elif [[ -z ${AZURE_VMGS_PATH} ]] && [[ ${AZURE_SECURITY_TYPE} == "TrustedLaunch" ]]; then
  echo "No VMGS path provided - using default TrsutedLaunch VMGS"
  AZURE_VMGS_PATH="${BLOBS_DIR}/trusted-launch-vmgs.vhd"
fi

SIZE=$(wc -c "${AZURE_IMAGE_PATH}" | cut -d " " -f1)

create_disk_with_vmgs() {
  az disk create \
    -n "${AZURE_DISK_NAME}" \
    -g "${AZURE_RESOURCE_GROUP_NAME}" \
    -l "${AZURE_REGION}" \
    --hyper-v-generation V2 \
    --os-type Linux \
    --upload-size-bytes "${SIZE}" \
    --sku standard_lrs \
    --upload-type UploadWithSecurityData \
    --security-type "${AZURE_DISK_SECURITY_TYPE}"
  az disk wait --created -n "${AZURE_DISK_NAME}" -g "${AZURE_RESOURCE_GROUP_NAME}"
  az disk list --output table --query "[?name == '${AZURE_DISK_NAME}' && resourceGroup == '${AZURE_RESOURCE_GROUP_NAME^^}']"
  DISK_SAS=$(az disk grant-access -n "${AZURE_DISK_NAME}" -g "${AZURE_RESOURCE_GROUP_NAME}" \
    --access-level Write --duration-in-seconds 86400 \
    ${AZURE_VMGS_PATH+"--secure-vm-guest-state-sas"})
  azcopy copy "${AZURE_IMAGE_PATH}" \
    "$(echo "${DISK_SAS}" | jq -r .accessSas)" \
    --blob-type PageBlob
  if [[ -z ${AZURE_VMGS_PATH} ]]; then
    echo "No VMGS path provided - skipping VMGS upload"
  else
    azcopy copy "${AZURE_VMGS_PATH}" \
      "$(echo "${DISK_SAS}" | jq -r .securityDataAccessSas)" \
      --blob-type PageBlob
  fi
  az disk revoke-access -n "${AZURE_DISK_NAME}" -g "${AZURE_RESOURCE_GROUP_NAME}"
}

create_disk_without_vmgs() {
  az disk create \
    -n "${AZURE_DISK_NAME}" \
    -g "${AZURE_RESOURCE_GROUP_NAME}" \
    -l "${AZURE_REGION}" \
    --hyper-v-generation V2 \
    --os-type Linux \
    --upload-size-bytes "${SIZE}" \
    --sku standard_lrs \
    --upload-type Upload
  az disk wait --created -n "${AZURE_DISK_NAME}" -g "${AZURE_RESOURCE_GROUP_NAME}"
  az disk list --output table --query "[?name == '${AZURE_DISK_NAME}' && resourceGroup == '${AZURE_RESOURCE_GROUP_NAME^^}']"
  DISK_SAS=$(az disk grant-access -n "${AZURE_DISK_NAME}" -g "${AZURE_RESOURCE_GROUP_NAME}" \
    --access-level Write --duration-in-seconds 86400)
  azcopy copy "${AZURE_IMAGE_PATH}" \
    "$(echo "${DISK_SAS}" | jq -r .accessSas)" \
    --blob-type PageBlob
  az disk revoke-access -n "${AZURE_DISK_NAME}" -g "${AZURE_RESOURCE_GROUP_NAME}"
}

create_disk() {
  if [[ -z ${AZURE_VMGS_PATH} ]]; then
    create_disk_without_vmgs
  else
    create_disk_with_vmgs
  fi
}

delete_disk() {
  az disk delete -y -n "${AZURE_DISK_NAME}" -g "${AZURE_RESOURCE_GROUP_NAME}"
}

create_image() {
  if [[ -n ${AZURE_VMGS_PATH} ]]; then
    return
  fi
  az image create \
    --resource-group "${AZURE_RESOURCE_GROUP_NAME}" \
    -l "${AZURE_REGION}" \
    -n "${AZURE_DISK_NAME}" \
    --hyper-v-generation V2 \
    --os-type Linux \
    --source "$(az disk list --query "[?name == '${AZURE_DISK_NAME}' && resourceGroup == '${AZURE_RESOURCE_GROUP_NAME^^}'] | [0].id" --output tsv)"
}

delete_image() {
  if [[ -n ${AZURE_VMGS_PATH} ]]; then
    return
  fi
  az image delete -n "${AZURE_DISK_NAME}" -g "${AZURE_RESOURCE_GROUP_NAME}"
}

# shellcheck disable=SC2086
create_sig_version() {
  if [[ -n ${AZURE_VMGS_PATH} ]]; then
    local DISK
    DISK="$(az disk list --query "[?name == '${AZURE_DISK_NAME}' && resourceGroup == '${AZURE_RESOURCE_GROUP_NAME^^}'] | [0].id" --output tsv)"
    local SOURCE="--os-snapshot ${DISK}"
  else
    local IMAGE
    IMAGE="$(az image list --query "[?name == '${AZURE_DISK_NAME}' && resourceGroup == '${AZURE_RESOURCE_GROUP_NAME^^}'] | [0].id" --output tsv)"
    local SOURCE="--managed-image ${IMAGE}"
  fi
  az sig create -l "${AZURE_REGION}" --gallery-name "${AZURE_GALLERY_NAME}" --resource-group "${AZURE_RESOURCE_GROUP_NAME}" || true
  az sig image-definition create \
    --resource-group "${AZURE_RESOURCE_GROUP_NAME}" \
    -l "${AZURE_REGION}" \
    --gallery-name "${AZURE_GALLERY_NAME}" \
    --gallery-image-definition "${AZURE_IMAGE_DEFINITION}" \
    --publisher "${AZURE_PUBLISHER}" \
    --offer "${AZURE_IMAGE_OFFER}" \
    --sku "${AZURE_SKU}" \
    --os-type Linux \
    --os-state generalized \
    --hyper-v-generation V2 \
    --features SecurityType="${AZURE_SECURITY_TYPE}" || true
  az sig image-version create \
    --resource-group "${AZURE_RESOURCE_GROUP_NAME}" \
    -l "${AZURE_REGION}" \
    --gallery-name "${AZURE_GALLERY_NAME}" \
    --gallery-image-definition "${AZURE_IMAGE_DEFINITION}" \
    --gallery-image-version "${AZURE_IMAGE_VERSION}" \
    --target-regions ${AZURE_REPLICATION_REGIONS} \
    ${AZURE_CVM_ENCRYPTION_ARGS} \
    --replica-count 1 \
    --replication-mode Full \
    ${SOURCE}
}

get_image_version_reference() {
  local is_community_gallery
  is_community_gallery=$(az sig show --gallery-name "${AZURE_GALLERY_NAME}" \
    --resource-group "${AZURE_RESOURCE_GROUP_NAME}" \
    --query 'sharingProfile.communityGalleryInfo.communityGalleryEnabled' \
    -o tsv)
  if [[ ${is_community_gallery} == "true" ]]; then
    get_community_image_version_reference
    return
  fi
  get_unshared_image_version_reference
}

get_community_image_version_reference() {
  local communityGalleryName
  communityGalleryName=$(az sig show --gallery-name "${AZURE_GALLERY_NAME}" \
    --resource-group "${AZURE_RESOURCE_GROUP_NAME}" \
    --query 'sharingProfile.communityGalleryInfo.publicNames[0]' \
    -o tsv)
  az sig image-version show-community \
    --public-gallery-name "${communityGalleryName}" \
    --gallery-image-definition "${AZURE_IMAGE_DEFINITION}" \
    --gallery-image-version "${AZURE_IMAGE_VERSION}" \
    --location "${AZURE_REGION}" \
    --query 'uniqueId' \
    -o tsv
}

get_unshared_image_version_reference() {
  az sig image-version show \
    --resource-group "${AZURE_RESOURCE_GROUP_NAME}" \
    --gallery-name "${AZURE_GALLERY_NAME}" \
    --gallery-image-definition "${AZURE_IMAGE_DEFINITION}" \
    --gallery-image-version "${AZURE_IMAGE_VERSION}" \
    --query id --output tsv
}

create_disk

if [[ ${CREATE_SIG_VERSION} == "YES" ]]; then
  create_image
  create_sig_version
  delete_image
  delete_disk
fi

image_reference=$(get_image_version_reference)
json=$(jq -ncS \
  --arg security_type "${security_type_short_name}" \
  --arg image_reference "${image_reference}" \
  '{"azure": {($security_type): $image_reference}}')
echo -n "${json}" > "${AZURE_JSON_OUTPUT}"
