#!/usr/bin/env bash
# narOS rootfs builder (NAROS.md §2.2): mmdebstrap against the pinned Debian snapshot,
# plus (optionally) the local Nucleic package pool. Produces a rootfs tar that CI turns
# into an OCI image (naros-base/…) or the VM payload tarball.
#
#   build-rootfs.sh <tier> <arch> [--pool DIR] [--out DIR] [--channel edge|stable]
#
# Needs mmdebstrap and either root or an unshare-capable user. The snapshot mirror serves
# stale Release files by design, hence Check-Valid-Until off (standard snapshot practice).
set -euo pipefail

OS_DIR="$(cd "$(dirname "$0")/.." && pwd)"
TIER="${1:?usage: build-rootfs.sh <tier> <arch> [--pool DIR] [--out DIR] [--channel C]}"
ARCH="${2:?missing arch (arm64|amd64)}"
shift 2

POOL="" OUT="$OS_DIR/dist" CHANNEL="${NAROS_CHANNEL:-edge}"
while [ $# -gt 0 ]; do
  case "$1" in
    --pool) POOL="$(cd "$2" && pwd)"; shift 2 ;;
    --out) OUT="$2"; shift 2 ;;
    --channel) CHANNEL="$2"; shift 2 ;;
    *) echo "unknown arg: $1" >&2; exit 2 ;;
  esac
done

VERSION="$(cat "$OS_DIR/VERSION")"
# Per-flavor base (NAROS.md §7.4): the VM DESKTOP flavor (N5) builds entirely from a pinned
# Debian FORKY (testing) snapshot for GNOME 50 — trixie ships only GNOME 48 — while every
# other tier, including the HEADLESS VM, stays on the trixie snapshot. One coherent pocket per
# rootfs, no trixie/forky ABI mixing. VARIANT is the os-release identity class
# (base/agent/runner/vm, §2.3); FLAVOR distinguishes the two VM rootfs builds within
# VARIANT=vm (headless vs desktop).
case "$TIER" in
  vm-desktop)
    SUITE="forky"; VARIANT="vm"; FLAVOR="desktop"
    SNAPSHOT="$(cat "$OS_DIR/SNAPSHOT.forky" 2>/dev/null || cat "$OS_DIR/SNAPSHOT")"
    ;;
  vm)
    SUITE="trixie"; VARIANT="vm"; FLAVOR="headless"
    SNAPSHOT="$(cat "$OS_DIR/SNAPSHOT")"
    ;;
  *)
    SUITE="trixie"; VARIANT="$TIER"; FLAVOR=""
    SNAPSHOT="$(cat "$OS_DIR/SNAPSHOT")"
    ;;
esac
MIRROR="https://snapshot.debian.org/archive/debian/${SNAPSHOT}/"
PROFILE="$OS_DIR/mkimage/profiles/$TIER.pkgs"
[ -f "$PROFILE" ] || { echo "no profile for tier '$TIER' ($PROFILE)" >&2; exit 2; }
DEBIAN_KEYRING="${DEBIAN_ARCHIVE_KEYRING:-/usr/share/keyrings/debian-archive-keyring.gpg}"
[ -r "$DEBIAN_KEYRING" ] || {
  echo "Debian archive keyring not found at $DEBIAN_KEYRING" >&2
  echo "install a current debian-archive-keyring or set DEBIAN_ARCHIVE_KEYRING" >&2
  exit 2
}

pkg_list() { grep -vE '^\s*(#|$)' "$1" | tr '\n' ',' | sed 's/,$//'; }
INCLUDE="$(pkg_list "$PROFILE")"

SOURCES=("deb [check-valid-until=no] $MIRROR $SUITE main")
if [ -n "$POOL" ]; then
  # Flat file:// repo over the pool; trusted because it is CI's own just-built artifact —
  # end-user trust comes from the signed hosted repo (repo/publish.sh), not this path.
  if [ ! -f "$POOL/Packages" ] || [ -n "$(find "$POOL" -name '*.deb' -newer "$POOL/Packages" 2>/dev/null)" ]; then
    (cd "$POOL" && dpkg-scanpackages --multiversion . > Packages)
  fi
  # Late packages (profiles/<tier>.late-pkgs) install via a finish hook AFTER every
  # Debian package is configured — mandatory for nash-default-shell: apt's configure
  # order is not deterministic, so a mid-transaction divert would run the remaining
  # Debian postinsts under nash (observed: ca-certificates' UTF-8 filenames mangled).
  # The divert must be the image's final configure step.
  LATE_DEBS=()
  LPROFILE="$OS_DIR/mkimage/profiles/$TIER.late-pkgs"
  if [ -f "$LPROFILE" ]; then
    while IFS= read -r pkg; do
      deb="$(ls "$POOL/${pkg}_"*.deb 2>/dev/null | head -1)"
      if [ -n "$deb" ]; then
        LATE_DEBS+=("$deb")
      elif [ "${NAROS_STRICT:-0}" = "1" ]; then
        echo "ERROR: late package $pkg absent from pool" >&2; exit 1
      else
        echo "WARNING: late package $pkg absent from pool — building WITHOUT it" >&2
      fi
    done < <(grep -vE '^\s*(#|$)' "$LPROFILE")
  fi

  NPROFILE="$OS_DIR/mkimage/profiles/$TIER.naros-pkgs"
  if [ -f "$NPROFILE" ]; then
    # Only request packages the pool actually carries: local builds legitimately lack
    # some (e.g. naros-keyring without the signing key). Loud per-package warning;
    # NAROS_STRICT=1 (CI) turns any gap into a hard failure.
    while IFS= read -r pkg; do
      if grep -qx "Package: $pkg" "$POOL/Packages"; then
        INCLUDE="$INCLUDE,$pkg"
      elif [ "${NAROS_STRICT:-0}" = "1" ]; then
        echo "ERROR: $pkg requested by $TIER tier but absent from pool" >&2; exit 1
      else
        echo "WARNING: $pkg absent from pool — building WITHOUT it" >&2
      fi
    done < <(grep -vE '^\s*(#|$)' "$NPROFILE")
  fi
  SOURCES+=("deb [trusted=yes] copy://$POOL ./")
fi

mkdir -p "$OUT"
TAR="$OUT/naros-$TIER-$VERSION-$ARCH.tar"

export NAROS_TIER="$TIER" NAROS_VERSION="$VERSION" NAROS_ARCH="$ARCH" \
       NAROS_CHANNEL="$CHANNEL" NAROS_SNAPSHOT="$SNAPSHOT" NAROS_SUITE="$SUITE" \
       NAROS_VARIANT="$VARIANT" NAROS_FLAVOR="$FLAVOR"

HOOKS=()
if [ "${#LATE_DEBS[@]}" -gt 0 ]; then
  LATE_ARGS=""
  for deb in "${LATE_DEBS[@]}"; do
    b="$(basename "$deb")"
    HOOKS+=(--customize-hook="copy-in $deb /tmp")
    LATE_ARGS="$LATE_ARGS /tmp/$b"
  done
  HOOKS+=(--customize-hook="chroot \"\$1\" dpkg -i$LATE_ARGS")
  HOOKS+=(--customize-hook="rm -f$(printf ' "$1"%s' $LATE_ARGS)")
fi
HOOKS+=(--customize-hook="$OS_DIR/mkimage/hooks/00-identity.sh"' "$1"')

echo "narOS $VERSION/$CHANNEL: tier=$TIER arch=$ARCH snapshot=$SNAPSHOT pool=${POOL:-none}"
mmdebstrap \
  --architectures="$ARCH" \
  --variant=apt \
  --keyring="$DEBIAN_KEYRING" \
  --include="$INCLUDE" \
  --aptopt='Acquire::Check-Valid-Until "false"' \
  "${HOOKS[@]}" \
  "$SUITE" "$TAR" "${SOURCES[@]}"

echo "built $TAR"
