#!/usr/bin/env bash
#
# slateos-onboard.sh — point a SlateOS (trixie-based) device at the SlateOS repo.
#
# Default mode (--via-proxy): keep ALL of Debian, but route it through your
# server's caching proxy (apt-cacher-ng), and add the SlateOS custom repo on top.
# Debian content stays Debian-signed; custom packages are signed by your key.
#
# Stages: CHECK (read-only) -> BACKUP -> APPLY, auto-rollback if apt update fails.
# Idempotent.
#
# Modes:
#   (default) / --via-proxy   Debian via your proxy + SlateOS custom repo  [recommended]
#   --keep-debian             Keep stock Debian mirrors + add SlateOS w/ pin priority
#   --replace                 Full cutover: disable Debian, SlateOS only (filtered!)
# Other flags:
#   --test     use slateos-test.sanddynamics.cloud
#   --check    check only, change nothing
#   --yes      no confirm prompt
#   --rollback restore most recent backup
#
set -euo pipefail

# ---- config -----------------------------------------------------------------
REPO_HOST_PROD="slateos-repo.sanddynamics.cloud"
REPO_HOST_TEST="slateos-test.sanddynamics.cloud"
KEY_FPR="CB22A54EF158F4261E86BDFBD0FCCAC3E8EA07EA"
SUITE="stable"; COMPONENT="main"
KEYRING="/etc/apt/keyrings/slateos.asc"
LISTFILE="/etc/apt/sources.list.d/slateos.list"
PINFILE="/etc/apt/preferences.d/slateos.pref"
DEBSRC="/etc/apt/sources.list.d/debian.sources"
BACKUP_DIR="/root"
# -----------------------------------------------------------------------------

MODE="proxy"; USE_TEST=0; CHECK_ONLY=0; ASSUME_YES=0; DO_ROLLBACK=0
for a in "$@"; do case "$a" in
  --via-proxy)   MODE="proxy" ;;
  --keep-debian) MODE="pin" ;;
  --replace)     MODE="replace" ;;
  --test)        USE_TEST=1 ;;
  --check)       CHECK_ONLY=1 ;;
  --yes|-y)      ASSUME_YES=1 ;;
  --rollback)    DO_ROLLBACK=1 ;;
  -h|--help)     grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
  *) echo "Unknown option: $a" >&2; exit 2 ;;
esac; done

c_red=$'\e[31m'; c_grn=$'\e[32m'; c_yel=$'\e[33m'; c_blu=$'\e[34m'; c_rst=$'\e[0m'
log(){ echo "${c_blu}>>${c_rst} $*"; }; ok(){ echo "${c_grn}OK${c_rst} $*"; }
warn(){ echo "${c_yel}WARN${c_rst} $*"; }; die(){ echo "${c_red}ERROR${c_rst} $*" >&2; exit 1; }
[[ $EUID -eq 0 ]] || die "Run as root (sudo $0 ...)."

REPO_HOST="$REPO_HOST_PROD"; [[ $USE_TEST -eq 1 ]] && REPO_HOST="$REPO_HOST_TEST"
BASE_URL="https://${REPO_HOST}"
CODENAME="trixie"; [[ -r /etc/os-release ]] && CODENAME=$(. /etc/os-release; echo "${VERSION_CODENAME:-trixie}")

# ---- rollback ---------------------------------------------------------------
if [[ $DO_ROLLBACK -eq 1 ]]; then
  BK=$(ls -1t "${BACKUP_DIR}"/apt-sources-backup-*.tgz 2>/dev/null | head -1 || true)
  [[ -n "$BK" ]] || die "No backup found in ${BACKUP_DIR}."
  log "Restoring apt config from: $BK"
  rm -f "$LISTFILE" "$PINFILE"
  tar xzf "$BK" -C / 2>/dev/null
  apt-get update; ok "Rolled back."; exit 0
fi

# ---- stage 1: CHECK ---------------------------------------------------------
log "Stage 1/3: CHECK (no changes) — mode=${MODE}, host=${REPO_HOST}, codename=${CODENAME}"
for t in wget curl gpg apt-get tar sed; do command -v "$t" >/dev/null || die "Missing tool: $t"; done
ok "Required tools present."

if [[ -r /etc/os-release ]]; then . /etc/os-release
  echo "   OS: ${PRETTY_NAME:-?} (id=${ID:-?} codename=${VERSION_CODENAME:-?})"
  case "${ID:-}${ID_LIKE:-}" in *debian*) ok "Debian-family OK." ;; *) warn "Not Debian-family — proceed only if sure." ;; esac
fi

log "Probing ${BASE_URL}/dists/${SUITE}/Release ..."
curl -fsSL --connect-timeout 10 --max-time 30 "${BASE_URL}/dists/${SUITE}/Release" -o /dev/null \
  || die "Cannot reach ${BASE_URL}. Is the tunnel/DNS/TLS up?"
ok "SlateOS custom repo reachable."

log "Checking signing-key fingerprint ..."
TMPKEY=$(mktemp)
curl -fsSL --max-time 30 "${BASE_URL}/slateos.asc" -o "$TMPKEY" || die "Cannot fetch ${BASE_URL}/slateos.asc"
GOT=$(gpg --show-keys --with-colons "$TMPKEY" 2>/dev/null | awk -F: '/^fpr:/{print $10; exit}')
echo "   published: ${GOT:-<none>}"; echo "   expected:  ${KEY_FPR}"
[[ "$GOT" == "$KEY_FPR" ]] || { rm -f "$TMPKEY"; die "KEY FINGERPRINT MISMATCH — refusing."; }
ok "Key fingerprint matches."

if [[ "$MODE" == "proxy" ]]; then
  log "Probing Debian caching proxy on your host ..."
  pc=$(curl -s -o /dev/null -w '%{http_code}' "${BASE_URL}/slateos/dists/${CODENAME}/InRelease")
  ps=$(curl -s -o /dev/null -w '%{http_code}' "${BASE_URL}/slateos-security/dists/${CODENAME}-security/InRelease")
  echo "   /slateos InRelease=${pc}   /slateos-security InRelease=${ps}"
  if [[ "$pc" == 200 && "$ps" == 200 ]]; then ok "Proxy is live for both archives."
  else rm -f "$TMPKEY"; die "Proxy not reachable (need 200/200). Run slateos-proxy-setup.sh on the server first."; fi
fi

echo; log "Planned changes (mode=${MODE}):"
case "$MODE" in
  proxy)
    echo "   + install key    -> ${KEYRING}"
    echo "   + custom source  -> ${LISTFILE}  (deb ${BASE_URL} ${SUITE} ${COMPONENT})"
    echo "   ~ rewrite Debian URIs -> ${BASE_URL}/slateos and ${BASE_URL}/slateos-security"
    echo "     (Debian stays Debian-signed; full archive served on-demand via your host)" ;;
  pin)
    echo "   + install key  -> ${KEYRING}"
    echo "   + custom source-> ${LISTFILE}"
    echo "   + pin          -> ${PINFILE} (SlateOS origin priority 1001, Debian kept)" ;;
  replace)
    echo "   + install key  -> ${KEYRING}"
    echo "   + custom source-> ${LISTFILE}"
    echo "   - disable Debian sources"
    warn "FILTERED mirror: packages outside the filter become uninstallable." ;;
esac
echo

[[ $CHECK_ONLY -eq 1 ]] && { rm -f "$TMPKEY"; ok "Check-only — no changes."; exit 0; }
if [[ $ASSUME_YES -ne 1 ]]; then
  read -r -p "Proceed? [y/N] " ans; [[ "$ans" =~ ^[Yy]$ ]] || { rm -f "$TMPKEY"; echo "Aborted."; exit 0; }
fi

# ---- stage 2: BACKUP --------------------------------------------------------
BACKUP="${BACKUP_DIR}/apt-sources-backup-$(date +%F-%H%M%S).tgz"
log "Stage 2/3: BACKUP -> ${BACKUP}"
tar czf "$BACKUP" /etc/apt/sources.list /etc/apt/sources.list.d /etc/apt/preferences.d 2>/dev/null || true
ok "Backed up /etc/apt."

# ---- stage 3: APPLY ---------------------------------------------------------
log "Stage 3/3: APPLY (mode=${MODE})"
install -d -m 0755 /etc/apt/keyrings
install -m 0644 "$TMPKEY" "$KEYRING"; rm -f "$TMPKEY"
echo "deb [signed-by=${KEYRING}] ${BASE_URL} ${SUITE} ${COMPONENT}" > "$LISTFILE"
ok "Installed SlateOS key + custom source."

case "$MODE" in
  proxy)
    # Rewrite stock Debian archive URIs to point at your caching proxy.
    rewrote=0
    for f in "$DEBSRC" /etc/apt/sources.list; do
      [[ -f "$f" ]] || continue
      if grep -qE 'deb\.debian\.org/debian|security\.debian\.org/debian-security' "$f"; then
        sed -i -E \
          -e "s@https?://security\.debian\.org/debian-security@${BASE_URL}/slateos-security@g" \
          -e "s@https?://deb\.debian\.org/debian@${BASE_URL}/slateos@g" "$f"
        ok "Rewrote Debian URIs in $f"; rewrote=1
      fi
    done
    [[ $rewrote -eq 1 ]] || warn "No stock deb.debian.org/security.debian.org entries found to rewrite (non-standard mirror?). Review manually."
    # Flag any other files still pointing straight at Debian (country mirrors etc.)
    others=$(grep -rlE 'deb\.debian\.org|//[a-z.]*\.debian\.org/debian' /etc/apt/sources.list.d 2>/dev/null | grep -v "$(basename "$DEBSRC")" || true)
    [[ -n "$others" ]] && warn "Other files still reference Debian directly (not rewritten): $others"
    ;;
  pin)
    cat > "$PINFILE" <<EOF
Package: *
Pin: origin ${REPO_HOST}
Pin-Priority: 1001
EOF
    ok "Wrote pin (Debian kept as fallback)." ;;
  replace)
    [[ -f "$DEBSRC" ]] && { mv "$DEBSRC" "${DEBSRC}.disabled"; ok "Disabled debian.sources"; }
    for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list; do
      [[ -f "$f" ]] || continue
      if grep -qE 'deb\.debian\.org|security\.debian\.org' "$f"; then
        sed -i -E 's@^[[:space:]]*deb([[:space:]].*(deb\.debian\.org|security\.debian\.org).*)@#deb\1@' "$f"
        ok "Commented Debian lines in $f"
      fi
    done ;;
esac

log "Running apt-get update ..."
if apt-get update; then ok "apt-get update succeeded."
else
  warn "apt-get update FAILED — rolling back."
  rm -f "$LISTFILE" "$PINFILE"; tar xzf "$BACKUP" -C / 2>/dev/null; apt-get update || true
  die "Cutover failed; restored from ${BACKUP}."
fi

# ---- verification -----------------------------------------------------------
echo; log "Verification:"
apt-cache policy curl 2>/dev/null | grep -q "$REPO_HOST" \
  && ok "Packages resolve via ${REPO_HOST}." || warn "Review 'apt-cache policy curl' manually."
if [[ "$MODE" == "replace" ]]; then
  REMOVES=$(LANG=C apt-get -s dist-upgrade 2>/dev/null | awk '/^Remv /{n++} END{print n+0}')
  [[ "${REMOVES:-0}" -gt 0 ]] && warn "${REMOVES} pkg(s) would be REMOVED on upgrade (filter gap)." || ok "No removals on upgrade."
fi
echo; ok "Done (mode=${MODE}). Backup: ${BACKUP}   Rollback: sudo $0 --rollback"
