#!/usr/bin/env bash
# Builds the agent sandbox image and its browser-equipped e2e sibling on this box.
set -euo pipefail

ROOT="${SANDBOX_HOST_ROOT:-$HOME/.local/share/overdeck-sandbox}"
CONTEXT="$ROOT/image-context"
FORCE=0
PULL_ONLY=0
PUBLISH=1
REGISTRY="${SANDBOX_CLUSTER_REGISTRY:-overdeck.k3s.local}"

while [ $# -gt 0 ]; do
  case "$1" in
    --force) FORCE=1; shift ;;
    --pull) PULL_ONLY=1; shift ;;
    --local-only) PUBLISH=0; shift ;;
    -h|--help) echo "usage: sandbox-image-build [--force] [--pull|--local-only]" >&2; exit 0 ;;
    *) echo "sandbox-image-build: unknown arg: $1" >&2; exit 2 ;;
  esac
done

[ "$PULL_ONLY" = 0 ] || [ "$PUBLISH" = 1 ] || {
  echo "sandbox-image-build: --pull and --local-only are exclusive" >&2
  exit 2
}
if [ "$PUBLISH" = 0 ]; then
  rm -f "$ROOT/agent-image-digest" "$ROOT/e2e-image-digest"
fi

[ -d "$CONTEXT" ] || { echo "sandbox-image-build: no image context at $CONTEXT" >&2; exit 2; }

HASH_LIB="$ROOT/lib/image-context.sh"
[ -r "$HASH_LIB" ] || { echo "sandbox-image-build: context digest helper missing at $HASH_LIB" >&2; exit 2; }
# shellcheck source=../lib/image-context.sh
. "$HASH_LIB"

HASH="$(sandbox_image_context_hash "$CONTEXT")"
AGENT_IMAGE="localhost/overdeck-agent-sandbox:$HASH"
E2E_IMAGE="localhost/overdeck-agent-sandbox-e2e:$HASH"
REGISTRY="${REGISTRY%/}"
AGENT_REMOTE="$REGISTRY/overdeck-agent-sandbox:$HASH"
E2E_REMOTE="$REGISTRY/overdeck-agent-sandbox-e2e:$HASH"

pull_by_digest() { # $1=remote tag $2=local tag $3=expected context hash $4=receipt file
  local remote=$1 local_image=$2 want_context=$3 receipt=$4 digest got_digest got_context
  if ! digest="$(podman manifest inspect --format '{{.Descriptor.digest}}' "$remote" 2>/dev/null)"; then
    echo "sandbox-image-build: registry pull miss for $remote" >&2
    return 4
  fi
  [[ "$digest" =~ ^sha256:[0-9a-f]{64}$ ]] || {
    echo "sandbox-image-build: registry returned an invalid digest for $remote: $digest" >&2
    return 1
  }
  if ! podman pull "$remote@$digest"; then
    echo "sandbox-image-build: registry pull miss for $remote@$digest" >&2
    return 4
  fi
  read -r got_digest got_context < <(podman image inspect --format '{{.Digest}} {{index .Labels "io.overdeck.sandbox-context"}}' "$remote@$digest")
  if [ "$got_digest" != "$digest" ] || [ "$got_context" != "$want_context" ]; then
    echo "sandbox-image-build: registry advertised a digest with context=${got_context:-missing}; refused $remote@$digest" >&2
    return 1
  fi
  podman tag "$remote@$digest" "$local_image"
  printf '%s@%s\n' "${remote%:$HASH}" "$digest" >"$receipt"
  echo "sandbox-image-build: pulled $remote@$digest"
}

push_by_digest() { # $1=local image $2=remote tag $3=receipt file
  local image=$1 remote=$2 receipt=$3 digest_file digest
  digest_file="$(mktemp)"
  trap 'rm -f "$digest_file"' RETURN
  podman tag "$image" "$remote"
  podman push --digestfile "$digest_file" "$remote"
  digest="$(<"$digest_file")"
  [[ "$digest" =~ ^sha256:[0-9a-f]{64}$ ]] || {
    echo "sandbox-image-build: registry did not return a digest for $remote" >&2
    return 1
  }
  printf '%s@%s\n' "${remote%:$HASH}" "$digest" >"$receipt"
  echo "sandbox-image-build: digest ${remote%:$HASH}@$digest"
}

publish_or_clear() { # $1=local image $2=remote tag $3=receipt file
  if [ "$PUBLISH" = 1 ]; then
    push_by_digest "$@"
  else
    echo "sandbox-image-build: kept $1 local; registry unavailable"
  fi
}

if [ "$PULL_ONLY" = 1 ]; then
  pull_by_digest "$AGENT_REMOTE" "$AGENT_IMAGE" "$HASH" "$ROOT/agent-image-digest" || exit $?
  pull_by_digest "$E2E_REMOTE" "$E2E_IMAGE" "$HASH" "$ROOT/e2e-image-digest" || exit $?
  printf '%s\n' "$AGENT_IMAGE" >"$ROOT/agent-image-tag"
  printf '%s\n' "$E2E_IMAGE" >"$ROOT/e2e-image-tag"
  exit 0
fi

build() {
  local image=$1 containerfile=$2
  shift 2
  if [ "$FORCE" = 0 ] && podman image exists "$image"; then
    echo "sandbox-image-build: $image already built"
    return 0
  fi
  systemd-run --user --scope --quiet --collect --slice=ci.slice \
    podman build --pull=newer --label "io.overdeck.sandbox-context=$HASH" --file "$CONTEXT/$containerfile" --tag "$image" "$@" "$CONTEXT"
}

build "$AGENT_IMAGE" Containerfile --build-arg "AGENT_UID=$(id -u)" --build-arg "AGENT_GID=$(id -g)"
printf '%s\n' "$AGENT_IMAGE" >"$ROOT/agent-image-tag"
publish_or_clear "$AGENT_IMAGE" "$AGENT_REMOTE" "$ROOT/agent-image-digest"

build "$E2E_IMAGE" Containerfile.e2e --build-arg "BASE_IMAGE=$AGENT_IMAGE"
printf '%s\n' "$E2E_IMAGE" >"$ROOT/e2e-image-tag"
publish_or_clear "$E2E_IMAGE" "$E2E_REMOTE" "$ROOT/e2e-image-digest"

podman image inspect "$AGENT_IMAGE" "$E2E_IMAGE" --format '{{.RepoTags}} {{.Size}}'

# Each content hash is a whole new pair of multi-GB images, so superseded tags are
# removed here rather than left to fill the disk. A tag still backing a live container
# is refused by podman and stays.
for repo in overdeck-agent-sandbox overdeck-agent-sandbox-e2e; do
  readarray -t tags < <(podman images --format '{{.Repository}}:{{.Tag}}' "localhost/$repo")
  for stale in ${tags[@]+"${tags[@]}"}; do
    if [ -n "$stale" ] && [ "$stale" != "localhost/$repo:$HASH" ]; then
      if podman rmi "$stale" >/dev/null 2>&1; then echo "sandbox-image-build: pruned $stale"; fi
    fi
  done
done
