#!/usr/bin/env bash
# Install the root-owned half of the AI OS: systemd system units and /usr/local
# helpers, from the paths each module opts into via its deck.module.json keys
# system_units / system_user_root / system_bin / system_sbin.
#
# Opt-in, never a glob: a directory named systemd/system inside any module used to
# install onto the workstation by name coincidence alone, so a module carrying root
# units meant for a different machine would land them here.
#
# Root units are COPIED, never symlinked: a root unit pointing into a
# user-writable repo would let any process that can write the repo run as root.
#
# usage: system-units.sh [--dry-run]
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
DRY=0
SUDO="${SUDO:-sudo}"
[[ "${1:-}" == "--dry-run" ]] && DRY=1

# host-config carries root units for the buildboxes; they reach a box only through
# the harden path, and installing them here would apply a builder's config to the
# workstation.
FORBIDDEN_PREFIX="host-config/"

run() {
  if ((DRY)); then echo "DRY: $*"; else "$@"; fi
}

install_tree() { # $1=source dir  $2=target dir  $3=mode — recurses so .d/ drop-in dirs survive
  local src="$1" dst="$2" mode="$3" f rel
  [[ -d "$src" ]] || return 0
  while IFS= read -r -d '' f; do
    rel="${f#"$src"/}"
    run "$SUDO" install -D -m "$mode" -o root -g root "$f" "$dst/$rel"
  done < <(find "$src" -type f -print0 | sort -z)
}

die() { echo "system-units: $*" >&2; exit 1; }

declared() { # $1=deck.module.json  $2=key -> one relative path per line
  jq -r --arg k "$2" '(.[$k] // []) | .[]' "$1"
}

declared_paths=()

install_declared() { # $1=module dir  $2=deck.module.json  $3=key  $4=target dir  $5=mode
  local mod="$1" desc="$2" key="$3" dst="$4" mode="$5" rel src
  while IFS= read -r rel; do
    [[ -n "$rel" ]] || continue
    case "$rel" in
      /*|*..*) die "$(basename "$mod")/$key: '$rel' must be a relative path inside the module" ;;
      "$FORBIDDEN_PREFIX"*) die "$(basename "$mod")/$key: '$rel' is under $FORBIDDEN_PREFIX and belongs to the buildbox harden path, not this workstation" ;;
    esac
    src="$mod/$rel"
    [[ -d "$src" ]] || die "$(basename "$mod")/$key declares '$rel' but $src does not exist"
    declared_paths+=("$src")
    install_tree "$src" "$dst" "$mode"
  done < <(declared "$desc" "$key")
}

for desc in "$ROOT"/modules/*/deck.module.json; do
  [[ -f "$desc" ]] || continue
  mod="$(dirname "$desc")"
  install_declared "$mod" "$desc" system_units     /etc/systemd/system 0644
  # user-root: user units and drop-ins that must NOT be user-writable. The user manager
  # reads /etc/systemd/user, and a drop-in there overrides the unit symlinked from the repo.
  install_declared "$mod" "$desc" system_user_root /etc/systemd/user   0644
  install_declared "$mod" "$desc" system_bin       /usr/local/bin      0755
  install_declared "$mod" "$desc" system_sbin      /usr/local/sbin     0755
done

# An undeclared unit directory installs nothing, which leaves root units looking
# present in the repo and absent on the machine.
while IFS= read -r d; do
  for known in ${declared_paths[@]+"${declared_paths[@]}"}; do
    [[ "$d" == "$known" ]] && continue 2
  done
  case "$d" in "$ROOT"/modules/buildbox/"$FORBIDDEN_PREFIX"*) continue ;; esac
  die "$d is not declared in its module's deck.module.json (add it to system_units/system_user_root/system_bin/system_sbin, or remove the directory)"
done < <(find "$ROOT"/modules -type d \( -path '*/systemd/system' -o -path '*/systemd/user-root' -o -name system-bin -o -name system-sbin \) | sort)

run "$SUDO" systemctl daemon-reload
# oomd reads ManagedOOM* once at start; without this a freshly installed drop-in is
# configured but unmonitored, which looks armed and is not.
run "$SUDO" systemctl try-restart systemd-oomd
echo "system-units: installed (enable individually with: sudo systemctl enable --now <unit>)"
