#!/usr/bin/env bash
set -euo pipefail

fail() { echo "D3 sfcrm dev image build: $*" >&2; exit 1; }

usage() {
  cat <<'EOF'
Usage:
  build-d3-sfcrm-dev-image.sh
  build-d3-sfcrm-dev-image.sh --local-only --tag-prefix PREFIX \
    --output-manifest PATH

The default mode publishes the sfcrm AppService development image to its owner
repository. Local-only mode instead loads an unsigned, non-promotable image into
the current Docker daemon. PREFIX must be a canonical lowercase Docker
repository prefix; the image is tagged as
PREFIX/sfcrm_app_service:dev-SOURCE_COMMIT. PATH receives a manifest that binds
the role to that exact source commit, local image reference, and Docker image ID.
EOF
}

mode="publish"
local_tag_prefix=""
output_manifest=""
while [[ $# -gt 0 ]]; do
  case "$1" in
    --local-only)
      mode="local-only"
      shift
      ;;
    --tag-prefix)
      [[ $# -ge 2 ]] || fail "--tag-prefix requires a value"
      local_tag_prefix="$2"
      shift 2
      ;;
    --output-manifest)
      [[ $# -ge 2 ]] || fail "--output-manifest requires a value"
      output_manifest="$2"
      shift 2
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *) fail "unsupported argument $1" ;;
  esac
done

if [[ "$mode" == "local-only" ]]; then
  [[ -n "$local_tag_prefix" ]] || fail "--local-only requires --tag-prefix"
  [[ -n "$output_manifest" ]] || fail "--local-only requires --output-manifest"
  [[ "$local_tag_prefix" =~ ^[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*$ ]] \
    || fail "--tag-prefix must be a canonical lowercase Docker repository prefix without a tag or digest"
  [[ "$output_manifest" != */ ]] || fail "--output-manifest must name a file"
else
  [[ -z "$local_tag_prefix" ]] || fail "--tag-prefix is valid only with --local-only"
  [[ -z "$output_manifest" ]] || fail "--output-manifest is valid only with --local-only"
fi

repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
source_sha="$(git -C "$repo_root" rev-parse HEAD)"
[[ "$source_sha" =~ ^[0-9a-f]{40}$ ]] || fail "source revision is not exact"

verify_source_identity() {
  [[ "$(git -C "$repo_root" rev-parse HEAD)" == "$source_sha" ]] \
    || fail "source revision changed during the build"
  [[ -z "$(git -C "$repo_root" status --porcelain)" ]] \
    || fail "source worktree must remain clean during the build"
}

verify_source_identity

for command in go docker jq; do
  command -v "$command" >/dev/null || fail "$command is required"
done
if [[ "$mode" == "publish" ]]; then
  command -v gh >/dev/null || fail "gh is required"
  gh auth status >/dev/null 2>&1 || fail "host gh CLI is not authenticated"
fi
docker buildx version >/dev/null || fail "Docker Buildx is required"

context="$(mktemp -d)"
metadata_root="$(mktemp -d)"
manifest_tmp=""
cleanup() {
  rm -rf "$context" "$metadata_root"
  [[ -z "$manifest_tmp" ]] || rm -f -- "$manifest_tmp"
}
trap cleanup EXIT

build_workspace="off"
if [[ "$mode" == "local-only" ]]; then
  (
    cd "$metadata_root"
    GOWORK=off go work init \
      "$repo_root/appservices/sfcrm" \
      "$repo_root/sfcrm/querycodec" \
      "$repo_root/sfcrm/scim" \
      "$repo_root/sfcrm/wire"
  )
  build_workspace="$metadata_root/go.work"
fi

mkdir -p "$context/appservices/sfcrm"
(
  cd "$repo_root/appservices/sfcrm"
  GOWORK="$build_workspace" CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath \
    -ldflags="-s -w -X main.version=$source_sha -X main.commit=$source_sha" \
    -o "$context/appservices/sfcrm/sfcrm-appservice-binary" ./cmd/sfcrm-appservice
)
cp "$repo_root/appservices/sfcrm/Dockerfile" "$context/Dockerfile"
verify_source_identity

local_image_field() {
  local image="$1"
  local jq_field="$2"
  docker image inspect "$image" | jq -er ".[0]$jq_field"
}

verify_local_image() {
  local image_ref="$1"
  local image_id="$2"
  local resolved_id revision release_class image_role image_os image_architecture

  resolved_id="$(local_image_field "$image_ref" '.Id')"
  [[ "$resolved_id" == "$image_id" ]] \
    || fail "sfcrm_app_service local image reference drifted from $image_id to $resolved_id"
  revision="$(local_image_field "$image_id" '.Config.Labels["org.opencontainers.image.revision"]')"
  [[ "$revision" == "$source_sha" ]] \
    || fail "sfcrm_app_service local image revision disagrees with source"
  release_class="$(local_image_field "$image_id" '.Config.Labels["dev.nexuscrm.release.class"]')"
  [[ "$release_class" == test-branch-candidate ]] \
    || fail "sfcrm_app_service local image is not marked as a Test branch candidate"
  image_role="$(local_image_field "$image_id" '.Config.Labels["dev.nexuscrm.app.role"]')"
  [[ "$image_role" == sfcrm-app-service ]] \
    || fail "sfcrm_app_service local image role label is $image_role"
  image_os="$(local_image_field "$image_id" '.Os')"
  image_architecture="$(local_image_field "$image_id" '.Architecture')"
  [[ "$image_os/$image_architecture" == linux/amd64 ]] \
    || fail "sfcrm_app_service local image platform is $image_os/$image_architecture"
}

write_local_manifest() {
  local image_ref="$1"
  local image_id="$2"
  local output_dir output_name

  output_dir="$(dirname -- "$output_manifest")"
  output_name="$(basename -- "$output_manifest")"
  mkdir -p -- "$output_dir"
  output_dir="$(cd -- "$output_dir" && pwd)"
  manifest_tmp="$(mktemp "$output_dir/.$output_name.XXXXXX")"

  verify_local_image "$image_ref" "$image_id"
  verify_source_identity
  jq -n --arg source_commit "$source_sha" --arg image_ref "$image_ref" \
    --arg image_id "$image_id" \
    '{schema:"nexuscrm.d3-local-images/v1",release_class:"test-only-unsigned-non-promotable",source_commit:$source_commit,images:([{role:"sfcrm_app_service",image_ref:$image_ref,image_id:$image_id,source_commit:$source_commit}]|sort_by(.role))}' \
    > "$manifest_tmp"
  mv -- "$manifest_tmp" "$output_dir/$output_name"
  manifest_tmp=""
  echo "[dev-image] wrote local-only manifest $output_dir/$output_name"
}

if [[ "$mode" == "publish" ]]; then
  metadata="$metadata_root/published-image.json"
  registry_user="${GITHUB_ACTOR:-$(gh api user --jq .login)}"
  gh auth token | docker login ghcr.io --username "$registry_user" --password-stdin >/dev/null
  image="ghcr.io/pingcap-inc/nexuscrm-sfcrm-appservice"
  docker buildx build \
    --platform linux/amd64 \
    --provenance=false \
    --sbom=true \
    --push \
    --build-arg "SOURCE_SHA=$source_sha" \
    --tag "$image:$source_sha" \
    --metadata-file "$metadata" \
    -f "$context/Dockerfile" "$context" >/dev/null
  digest="$(jq -er '."containerimage.digest"' "$metadata")"
  [[ "$digest" =~ ^sha256:[0-9a-f]{64}$ ]] || fail "image digest is not canonical"
  jq -cn --arg role sfcrm_app_service --arg image "$image" --arg digest "$digest" \
    --arg source_commit "$source_sha" \
    '{role:$role,oci_repository:$image,digest:$digest,source_commit:$source_commit}'
else
  image_ref="$local_tag_prefix/sfcrm_app_service:dev-$source_sha"
  echo "[sfcrm-dev-image] loading unsigned local-only sfcrm_app_service for $source_sha"
  docker buildx build \
    --platform linux/amd64 \
    --provenance=false \
    --load \
    --build-arg "SOURCE_SHA=$source_sha" \
    --label "dev.nexuscrm.app.role=sfcrm-app-service" \
    --label "dev.nexuscrm.release.class=test-branch-candidate" \
    --tag "$image_ref" \
    -f "$context/Dockerfile" "$context" >/dev/null
  image_id="$(local_image_field "$image_ref" '.Id')"
  [[ "$image_id" =~ ^sha256:[0-9a-f]{64}$ ]] \
    || fail "sfcrm_app_service returned no canonical local image ID"
  verify_local_image "$image_ref" "$image_id"
  write_local_manifest "$image_ref" "$image_id"
fi
