#!/bin/bash
# Phase-2 first-boot provisioning for the Nucleic Linux base (docs/LINUX_VM.md §base build).
#
# Runs ONCE, as root, from `nucleic-firstboot.service` the very first time the installed rootfs boots.
# It turns a bare Ubuntu rootfs into a Nucleic Linux base: a GNOME / Mutter Wayland desktop with
# auto-login (GDM) so the host-side computer-use surface has a real screen to see and drive, PLUS the
# accessibility (AT-SPI) bus turned on system-wide — the precondition for the in-guest semantic agent
# (docs/LINUX_VM_SEMANTIC_AGENT.md). It also installs a dev toolchain. When it finishes it writes
# /var/lib/nucleic/provisioned (the host's readiness signal), disables itself, and lets the host power
# the guest off to seal the base.
#
# Why GNOME/Mutter (not labwc): the semantic agent's geometry path (Phase 2/3) is Mutter-specific
# (vendored-Newton subset), so the base is pinned to the latest LTS's GNOME/Mutter — Ubuntu 26.04 LTS
# "resolute" ships GNOME 50 (the pinned artifacts in Project.swift). A GNOME bump is a scoped port.
#
# The guest is a disposable, NAT-isolated VM — the `nucleic` account's password is a lab credential.
set -x
exec >>/var/log/nucleic-provision.log 2>&1
export DEBIAN_FRONTEND=noninteractive

log() { echo "[nucleic-provision $(date -u +%H:%M:%S)] $*"; }
progress() { echo "$*" > /var/lib/nucleic/progress; }   # tail'd by the host for a live sub-step

mkdir -p /var/lib/nucleic

log "waiting for network (NAT DHCP)"
progress "Waiting for network"
for i in $(seq 1 60); do
    if getent hosts archive.ubuntu.com >/dev/null 2>&1; then break; fi
    sleep 2
done

log "apt update"
progress "Updating package lists"
apt-get update -y || true

# GNOME 49 Wayland session (gnome-shell/Mutter + GDM auto-login), a couple of GTK4 apps so a fresh
# screenshot isn't empty, XWayland for X apps, the accessibility stack (at-spi2-core = the AT-SPI D-Bus
# registry the semantic agent consumes), and the bits a host-driven desktop needs (virtio_gpu lives in
# linux-modules-extra for our external cloud kernel). Deliberately NOT the full ubuntu-desktop (no
# snaps) — just Shell + the reference apps.
log "installing the GNOME Wayland desktop"
progress "Installing the desktop (GNOME / Mutter + GDM)"
# NB: kernel modules (incl. virtio_gpu) are baked into the rootfs by the bootstrap to match our
# external kernel — do NOT apt-install linux-modules here (the kernel isn't dpkg-managed, so apt would
# drag in a whole second linux-image).
apt-get install -y --no-install-recommends \
    gnome-session gnome-shell gnome-shell-extension-prefs gdm3 \
    gnome-console gnome-text-editor \
    xwayland dbus-user-session libgl1-mesa-dri \
    at-spi2-core gsettings-desktop-schemas dconf-cli \
    fonts-dejavu-core \
    || log "WARN: some desktop packages failed to install"

log "installing the dev toolchain"
progress "Installing the dev toolchain"
apt-get install -y --no-install-recommends \
    build-essential git curl ca-certificates openssh-client \
    python3 python3-pip python3-venv \
    python3-gi gir1.2-atspi-2.0 \
    nodejs npm \
    || log "WARN: some toolchain packages failed to install"

# Firefox, preinstalled as a reference browser for the computer-use / semantic agent. Ubuntu's own
# `firefox` apt package is only a transitional shim that installs the Snap, but this base is
# deliberately snap-free (see the desktop block above), so we pull the real .deb from Mozilla's
# official APT repo and pin it above the Ubuntu shim. curl + ca-certificates come from the toolchain
# block above.
log "installing Firefox (Mozilla APT repo, .deb — no snap)"
progress "Installing Firefox"
install -d -m 0755 /etc/apt/keyrings
if curl -fsSL https://packages.mozilla.org/apt/repo-signing-key.gpg -o /etc/apt/keyrings/packages.mozilla.org.asc; then
    echo 'deb [signed-by=/etc/apt/keyrings/packages.mozilla.org.asc] https://packages.mozilla.org/apt mozilla main' \
        > /etc/apt/sources.list.d/mozilla.list
    # Prefer Mozilla's builds over the Ubuntu snap transitional package.
    cat > /etc/apt/preferences.d/mozilla <<EOF
Package: *
Pin: origin packages.mozilla.org
Pin-Priority: 1000
EOF
    apt-get update -y || true
    apt-get install -y --no-install-recommends firefox \
        || log "WARN: Firefox failed to install"
else
    log "WARN: could not fetch Mozilla signing key; skipping Firefox"
fi

# The interactive desktop account GDM auto-logs into.
log "creating the nucleic account"
progress "Configuring the desktop session"
useradd -m -s /bin/bash -G sudo,video,input,render nucleic 2>/dev/null || true
echo 'nucleic:nucleic' | chpasswd

# GDM auto-login straight into the GNOME Wayland session (no password prompt), so the host surface
# sees a live desktop. WaylandEnable=true is GDM's default; we assert it so the session is never X11
# (the semantic agent + Phase 2/3 geometry are Wayland-native).
mkdir -p /etc/gdm3
cat > /etc/gdm3/custom.conf <<EOF
[daemon]
WaylandEnable=true
AutomaticLoginEnable=true
AutomaticLogin=nucleic
EOF

# System-wide dconf defaults (apply to the auto-login user without a per-user seed):
#   • toolkit-accessibility=true  — turns the AT-SPI bridge on for GTK/GNOME apps so their trees are
#     exposed to the semantic agent (the whole point of this base).
#   • no idle/lock/blank/sleep    — the host surface must always see a live, unlocked screen.
#   • suppress the GNOME welcome dialog so the first screenshot is the desktop, not a tour.
mkdir -p /etc/dconf/profile /etc/dconf/db/local.d
cat > /etc/dconf/profile/user <<EOF
user-db:user
system-db:local
EOF
cat > /etc/dconf/db/local.d/00-nucleic <<EOF
[org/gnome/desktop/interface]
toolkit-accessibility=true
# Pin integer display scale = 1 (no fractional scaling). This is the invariant that makes the
# compositor's window coordinates (via the geometry helper) equal the virtio-gpu scanout pixels the
# host captures and the USB pointer targets 1:1 — so ax_dump frames are directly clickable. The
# 1920x1200 mode already defaults to scale 1; assert it so a config change can't silently break
# coordinate alignment (docs/LINUX_VM_SEMANTIC_AGENT.md Phase 2).
scaling-factor=uint32 1

[org/gnome/mutter]
# Do NOT enable 'scale-monitor-framebuffer' (fractional scaling) — keep logical == physical pixels.
experimental-features=@as []

[org/gnome/desktop/session]
idle-delay=uint32 0

[org/gnome/desktop/screensaver]
lock-enabled=false
idle-activation-enabled=false

[org/gnome/settings-daemon/plugins/power]
sleep-inactive-ac-type='nothing'
sleep-inactive-battery-type='nothing'

[org/gnome/shell]
welcome-dialog-last-shown-version='99.0'
# Enable the Nucleic geometry helper extension (Phase 2) if it was baked in; harmless if absent.
disable-user-extensions=false
enabled-extensions=['nucleic-geometry@nucleic.xyz']
EOF
dconf update || log "WARN: dconf update failed"

# Qt apps read this env to enable their AT-SPI bridge (GTK does so automatically once the a11y bus is
# up). Chromium/Electron still need a per-launch --force-renderer-accessibility (handled by the agent).
if ! grep -q QT_ACCESSIBILITY /etc/environment 2>/dev/null; then
    echo 'QT_ACCESSIBILITY=1' >> /etc/environment
fi

# Enable the AT-SPI semantic agent (systemd USER service) for every graphical session, if it was baked
# into the base (docs/LINUX_VM_SEMANTIC_AGENT.md Phase 1). It binds vsock 2036 inside the session.
if [ -f /etc/systemd/user/nucleic-a11y-agent.service ]; then
    log "enabling the AT-SPI semantic agent (user service)"
    systemctl --global enable nucleic-a11y-agent.service 2>/dev/null || log "WARN: could not enable a11y agent"
fi

# Skip GNOME's first-run initial-setup + tour for the auto-login user.
mkdir -p /home/nucleic/.config
echo yes > /home/nucleic/.config/gnome-initial-setup-done
# Autostart a GTK4 app so a fresh screenshot shows a real window, not an empty desktop.
mkdir -p /home/nucleic/.config/autostart
cat > /home/nucleic/.config/autostart/nucleic-welcome.desktop <<EOF
[Desktop Entry]
Type=Application
Name=Nucleic Welcome
Exec=gnome-text-editor
X-GNOME-Autostart-enabled=true
EOF
chown -R nucleic:nucleic /home/nucleic/.config

systemctl enable gdm3 systemd-networkd 2>/dev/null || true
systemctl set-default graphical.target 2>/dev/null || true

# A default PATH for host exec (the vsock agent runs commands under /bin/sh; ensure the toolchain
# resolves for non-login shells).
cat > /etc/profile.d/nucleic-path.sh <<EOF
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
EOF

# Don't rerun.
log "sealing the base"
progress "Finalizing"
systemctl disable nucleic-firstboot.service 2>/dev/null || true
apt-get clean || true
sync
touch /var/lib/nucleic/provisioned
log "provisioning complete"
