#!/usr/bin/env bash
# publish-kernel.sh
#
# Package an entry from the local banger kernel catalog as a tar.zst,
# upload it to the public R2 bucket, and patch internal/kernelcat/catalog.json
# with the resulting URL + sha256 + size. Run after `banger kernel import`.
#
# Usage:
#   scripts/publish-kernel.sh <name> [--description "..."]
#
# Environment overrides:
#   RCLONE_REMOTE         rclone remote to upload through (default: r2)
#   RCLONE_BUCKET         R2 bucket name                  (default: banger-kernels)
#   BASE_URL              public URL prefix for the bucket (default: https://kernels.thaloco.com)
#   BANGER_KERNELS_DIR    local catalog directory          (default: ~/.local/state/banger/kernels)

set -euo pipefail

log() { printf '[publish-kernel] %s\n' "$*" >&2; }
die() { log "$*"; exit 1; }

usage() {
  cat <<EOF
usage: scripts/publish-kernel.sh <name> [--description "<text>"]

Reads the locally-imported kernel at \$BANGER_KERNELS_DIR/<name>/, packages
it as <name>-<arch>.tar.zst, uploads to R2, and updates
internal/kernelcat/catalog.json.

Run \`banger kernel import <name> --from <build-dir> --distro <d> --arch <a>\`
first.
EOF
}

RCLONE_REMOTE="${RCLONE_REMOTE:-r2}"
RCLONE_BUCKET="${RCLONE_BUCKET:-banger-kernels}"
BASE_URL="${BASE_URL:-https://kernels.thaloco.com}"
BANGER_KERNELS_DIR="${BANGER_KERNELS_DIR:-$HOME/.local/state/banger/kernels}"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
CATALOG_FILE="$REPO_ROOT/internal/kernelcat/catalog.json"

NAME=""
DESCRIPTION=""
while [[ $# -gt 0 ]]; do
  case "$1" in
    -d|--description) DESCRIPTION="${2:-}"; shift 2;;
    -h|--help) usage; exit 0;;
    --) shift; break;;
    -*) die "unknown flag: $1";;
    *)
      if [[ -z "$NAME" ]]; then
        NAME="$1"; shift
      else
        die "unexpected positional arg: $1"
      fi
      ;;
  esac
done
[[ -n "$NAME" ]] || { usage; exit 1; }

for tool in jq rclone tar zstd sha256sum stat curl; do
  command -v "$tool" >/dev/null 2>&1 || die "missing required tool: $tool"
done
[[ -f "$CATALOG_FILE" ]] || die "catalog file not found: $CATALOG_FILE"

SRC="$BANGER_KERNELS_DIR/$NAME"
[[ -d "$SRC" ]] || die "$SRC does not exist; run 'banger kernel import $NAME --from <dir>' first"
[[ -f "$SRC/vmlinux" ]] || die "$SRC/vmlinux missing"
[[ -f "$SRC/manifest.json" ]] || die "$SRC/manifest.json missing"

DISTRO="$(jq -r '.distro // ""' "$SRC/manifest.json")"
ARCH="$(jq -r '.arch // ""' "$SRC/manifest.json")"
KERNEL_VERSION="$(jq -r '.kernel_version // ""' "$SRC/manifest.json")"
[[ -n "$ARCH" ]] || ARCH="x86_64"

STAGE="$(mktemp -d)"
trap 'rm -rf "$STAGE"' EXIT

TARBALL_NAME="${NAME}-${ARCH}.tar.zst"
TARBALL="$STAGE/$TARBALL_NAME"

INCLUDES=(vmlinux)
[[ -f "$SRC/initrd.img" ]] && INCLUDES+=(initrd.img)
[[ -d "$SRC/modules" ]] && INCLUDES+=(modules)

log "packaging ${INCLUDES[*]} from $SRC"
( cd "$SRC" && tar -cf - "${INCLUDES[@]}" ) | zstd -19 --long -T0 -q -o "$TARBALL"

SHA256="$(sha256sum "$TARBALL" | awk '{print $1}')"
SIZE="$(stat -c '%s' "$TARBALL")"
HUMAN_SIZE="$(numfmt --to=iec --suffix=B "$SIZE" 2>/dev/null || echo "${SIZE}B")"
log "tarball $TARBALL_NAME: $HUMAN_SIZE, sha256 $SHA256"

log "uploading to $RCLONE_REMOTE:$RCLONE_BUCKET/$TARBALL_NAME"
rclone copyto "$TARBALL" "$RCLONE_REMOTE:$RCLONE_BUCKET/$TARBALL_NAME"

URL="$BASE_URL/$TARBALL_NAME"
log "verifying $URL is reachable"
HEAD_STATUS="$(curl -fsSI -o /dev/null -w '%{http_code}' "$URL" || true)"
if [[ "$HEAD_STATUS" != "200" ]]; then
  die "uploaded tarball is not publicly reachable at $URL (HTTP $HEAD_STATUS); check bucket public-access config"
fi

log "patching $CATALOG_FILE"
NEW_ENTRY="$(jq -n \
  --arg name "$NAME" \
  --arg distro "$DISTRO" \
  --arg arch "$ARCH" \
  --arg kver "$KERNEL_VERSION" \
  --arg url "$URL" \
  --arg sha "$SHA256" \
  --argjson size "$SIZE" \
  --arg desc "$DESCRIPTION" \
  '{
    name: $name,
    distro: $distro,
    arch: $arch,
    kernel_version: $kver,
    tarball_url: $url,
    tarball_sha256: $sha,
    size_bytes: $size,
    description: $desc
  } | with_entries(select(.value != null and .value != ""))')"

CATALOG_TMP="$(mktemp)"
jq --arg name "$NAME" --argjson new "$NEW_ENTRY" '
  .version = (.version // 1)
  | .entries = (((.entries // []) | map(select(.name != $name))) + [$new])
  | .entries |= sort_by(.name)
' "$CATALOG_FILE" > "$CATALOG_TMP"
mv "$CATALOG_TMP" "$CATALOG_FILE"

log "done"
log "next steps:"
log "  git diff -- $CATALOG_FILE"
log "  git add $CATALOG_FILE && git commit -m 'kernel catalog: add/update $NAME'"
log "  make build   # rebuild banger so the new catalog is embedded"
