#!/bin/bash
# The upgrade that failed, reproduced on purpose.
#
# A real classroom does not arrive new, and the clean-install test was
# passing on a machine that had never been anything else. This builds a
# machine that has: the Hyprland experiment package actually installed,
# with sway, waybar and mako behind it, a Waybar activated through
# graphical-session, an autostart entry, a device.conf still asking for a
# compositor that is gone, and a Plasma profile belonging to a different
# desktop. Then it installs the classroom package over the top and asks
# whether the room converged.
#
#   tools/lab/dirty-upgrade [NEW.deb] [OLD.deb]
#   CHECK_ARGS=--restart tools/lab/dirty-upgrade   ask twice, either side
#                                                  of a logout
#   tools/lab/dirty-upgrade NEW.deb dist/ekklesia-classroom_0.2.2_amd64.deb
#                                  the day the desktops move from 0.2.2
#
# The container is restarted between the install and the session, because
# a room takes its upgrade and reboots.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
LAB="$ROOT/tools/lab/lab"
NAME="${LAB_NAME:-classroom-v2-lab}"

NEW="${1:-$(ls -t "$ROOT"/dist/ekklesia-classroom_*_amd64.deb | grep -v '~' | head -1)}"
OLD="${2:-$ROOT/dist/ekklesia-classroom_0.1.45~hypr3_amd64.deb}"

step() { printf '\n\033[1m== %s\033[0m\n' "$1"; }

step "a fresh machine"
"$LAB" up >/dev/null

docker exec "$NAME" sh -c '
    printf "#!/bin/sh\nexit 101\n" > /usr/sbin/policy-rc.d
    chmod +x /usr/sbin/policy-rc.d
    apt-get update -qq >/dev/null 2>&1
    # The old package schedules a provisioning run that would go to
    # backports for Hyprland. The lab is not testing that.
    systemctl mask ekklesia-classroom-provision.service >/dev/null 2>&1'

# Which version is installed, fully configured, or the rehearsal stops.
# Every step below is a question about one particular release, and a
# step that quietly left the machine on another one answers for the
# wrong release — which is how the 0.2.2 rehearsal once passed on 0.2.2.
require_installed() {
    local want got
    want=$(dpkg-deb --field "$1" Version)
    got=$(docker exec "$NAME" dpkg-query -W -f='${Version} ${db:Status-Abbrev}' ekklesia-classroom 2>/dev/null || true)
    echo "  installed: $got"

    if [[ "$got" != "$want ii " ]]; then
        echo "  $(basename "$1") DID NOT INSTALL: [$got], wanted [$want ii]" >&2
        echo "  Nothing after this would be a question about $want." >&2
        exit 1
    fi
}

step "the old classroom: $(basename "$OLD")"
# With apt, the way a room installs anything, so an old package's own
# dependencies are resolved rather than forced.
docker exec "$NAME" sh -c "
    DEBIAN_FRONTEND=noninteractive apt-get install -y -qq '/srv/classroom/${OLD#$ROOT/}' >/dev/null 2>&1
    true" || true
require_installed "$OLD"

step "and everything a used machine accumulates"
docker exec -i "$NAME" bash <<'DIRT'
set -e

# Waybar and mako, activated the way the real room had them. Neither
# package ships a unit in trixie, so the units themselves are part of what
# somebody added — and one of the three activation directories is the one
# the broken room actually used.
for unit in waybar mako; do
    cat > "/etc/systemd/user/$unit.service" <<UNIT
[Unit]
Description=$unit (left over from the old classroom shell)
PartOf=graphical-session.target
After=graphical-session.target

[Service]
ExecStart=/usr/bin/$unit
Restart=on-failure

[Install]
WantedBy=graphical-session.target
UNIT
done

for dir in /etc/systemd/user /etc/xdg/systemd/user \
           /home/classroom/.config/systemd/user; do
    mkdir -p "$dir/graphical-session.target.wants"
    ln -sf /etc/systemd/user/waybar.service \
        "$dir/graphical-session.target.wants/waybar.service"
    ln -sf /etc/systemd/user/mako.service \
        "$dir/graphical-session.target.wants/mako.service"
done

# An autostart entry as well, for a bar that is started two ways.
mkdir -p /home/classroom/.config/autostart
cat > /home/classroom/.config/autostart/waybar.desktop <<'ENTRY'
[Desktop Entry]
Type=Application
Name=Waybar
Exec=waybar
ENTRY

# The room still asking for a compositor that is gone, with a broker
# password in the same file to prove the migration does not rewrite it.
sed -i 's/^CHANNEL=.*/CHANNEL=hyprland/' /etc/ekklesia-classroom/device.conf
{
    echo "COMPOSITOR=hyprland"
    echo "HYPR_APPEARANCE=visual"
} >> /etc/ekklesia-classroom/device.conf
sed -i 's/^MQTT_HOST=.*/MQTT_HOST=broker.example.invalid/' /etc/ekklesia-classroom/device.conf
sed -i 's/^MQTT_PASSWORD=.*/MQTT_PASSWORD=keep-me-exactly/' /etc/ekklesia-classroom/device.conf

# A Plasma profile from a different desktop: someone else's palette, in
# both of the places that outrank the package.
mkdir -p /home/classroom/.config/kdedefaults
cat > /home/classroom/.config/kdedefaults/kdeglobals <<'KDE'
[General]
ColorScheme=BreezeLight

[Icons]
Theme=breeze

[KDE]
LookAndFeelPackage=org.kde.breeze.desktop
KDE
cat > /home/classroom/.config/kdeglobals <<'KDE'
[General]
ColorScheme=BreezeLight

[Colors:Window]
BackgroundNormal=239,240,241
ForegroundNormal=35,38,41

[KDE]
LookAndFeelPackage=org.kde.breeze.desktop
KDE

# And a stock panel, already built, of the kind the broken upgrade made.
cat > /home/classroom/.config/plasma-org.kde.plasma.desktop-appletsrc <<'PANEL'
[Containments][1]
plugin=org.kde.plasma.folder

[Containments][2]
plugin=org.kde.panel

[Containments][2][Applets][3]
plugin=org.kde.plasma.kickoff

[Containments][2][Applets][4]
plugin=org.kde.plasma.pager

[Containments][2][Applets][5]
plugin=org.kde.plasma.icontasks

[Containments][2][Applets][5][Configuration][General]
launchers=applications:systemsettings.desktop,applications:org.kde.discover.desktop,preferred://browser

[Containments][2][Applets][6]
plugin=org.kde.plasma.systemtray
PANEL

# A user-level power profile that would suspend the room.
cat > /home/classroom/.config/powermanagementprofilesrc <<'POWER'
[AC][SuspendSession]
idleTime=1800000
suspendType=1

[AC][DPMSControl]
idleTime=600
POWER

chown -R classroom:classroom /home/classroom/.config
echo "  dirt in place"
DIRT

step "the new classroom: $(basename "$NEW")"
docker exec "$NAME" sh -c '
    systemctl unmask ekklesia-classroom-provision.service >/dev/null 2>&1 || true'
# The way ekklesia-classroom-update installs it: apt, not dpkg. The
# package conflicts with khelpcenter since 0.2.6.2, and dpkg -i refuses a
# conflict where apt resolves it — so from 0.2.2 this step used to leave
# the machine on 0.2.2 and every check below was asked of the old release.
docker exec "$NAME" sh -c "
    DEBIAN_FRONTEND=noninteractive apt-get install -y '/srv/classroom/${NEW#$ROOT/}' 2>&1 \
        | grep -E '^\[classroom-migrate\]' || true
    rm -f /usr/sbin/policy-rc.d
    true" || true
require_installed "$NEW"

step "and the room reboots"
"$LAB" reboot

step "the session"
"$LAB" session >/dev/null
sleep 6

step "did it converge?"
converged=0
"$ROOT/tools/lab/converged-check" ${CHECK_ARGS:-} || converged=1

step "and again, installed a second time over itself"
# An update that changes nothing must change nothing. The migration runs
# again here, over a machine that is already converged.
docker exec "$NAME" sh -c "
    DEBIAN_FRONTEND=noninteractive apt-get install --reinstall -y -qq '/srv/classroom/${NEW#$ROOT/}' >/dev/null 2>&1
    true" || true
require_installed "$NEW"
before=$(docker exec "$NAME" sh -c 'cat /etc/ekklesia-classroom/device.conf | sha256sum')
"$LAB" plasma-down >/dev/null
"$LAB" plasma-up >/dev/null
sleep 4
after=$(docker exec "$NAME" sh -c 'cat /etc/ekklesia-classroom/device.conf | sha256sum')
[[ "$before" == "$after" ]] \
    && echo "  device.conf unchanged by the second install" \
    || { echo "  DEVICE.CONF CHANGED ON REINSTALL"; converged=1; }
"$ROOT/tools/lab/converged-check" || converged=1

step "and the room's own theme choice survives a logout"
# Deleting the look and feel defaults every login was measured to make
# Plasma re-apply the look and feel and throw the room's chosen palette
# away with it. This is the check that stops that coming back.
# With the session's own cascade. plasma-apply-colorscheme writes the
# chosen name into ~/.config/kdedefaults, which is in XDG_CONFIG_DIRS for
# a session and not for a bare command, so asking without it reports
# nothing and means nothing.
theme() {
    local dirs
    dirs=$(docker exec "$NAME" bash -c 'p=$(pgrep -x plasmashell | head -1); tr "\0" "\n" < /proc/$p/environ | grep "^XDG_CONFIG_DIRS=" | cut -d= -f2-')
    docker exec -u classroom -e XDG_RUNTIME_DIR=/run/user/1000 \
        -e DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \
        -e WAYLAND_DISPLAY=wayland-0 -e "XDG_CONFIG_DIRS=$dirs" "$NAME" \
        /usr/local/bin/classroom-theme "$1" 2>/dev/null | tail -1
}
theme light >/dev/null
"$LAB" plasma-down >/dev/null
"$LAB" plasma-up >/dev/null
sleep 4
chosen=$(theme show)
if [[ "$chosen" == EkklesiaLight ]]; then
    echo "  light survived the logout"
else
    echo "  THE ROOM'S THEME CHOICE WAS LOST: $chosen"
    converged=1
fi
theme dark >/dev/null
echo "  back to $(theme show)"

step "and every desktop has the classroom's wallpaper, however it was made"
# Tiny Pebbles, 0.2.2 -> 0.2.6.4: a desktop Plasma made after the layout
# had run came up with Plasma's own wallpaper, and nothing here noticed,
# because every session this rehearsal started had its screen from the
# beginning. This makes Plasma build one by itself, and puts Tiny
# Pebbles' own file in front of a shell restart.
"$ROOT/tools/lab/wallpaper-check" || converged=1

step "and it still recovers"
"$ROOT/tools/lab/recovery-check" || converged=1

exit "$converged"
