#!/usr/bin/env sh
# opencode-skybox.install — pacman install hooks.
#
# post_install / post_upgrade: scaffold the agent server (idempotent;
#   converging the same state on reinstall/upgrade is a no-op). Upgrades from
#   the previous layout (home = the package dir) migrate the home first (below).
# pre_remove:          stop + disable the service before pacman removes files.
# post_remove:         teardown — config, sidecar, service account, group;
#                      the package dir (agent home + coding root) is kept.

# --- shared helpers (available in every hook; no top-level side effects) ---

group_exists() {
    getent group agent-workspace >/dev/null 2>&1
}

user_exists() {
    getent passwd opencode-agent >/dev/null 2>&1
}

agent_home() {
    getent passwd opencode-agent | cut -d: -f6
}

pkgdir=/var/lib/opencode-agent
newhome=$pkgdir/home
staging=/var/lib/.opencode-agent.migrate

# The migration has work to do while the passwd home still points at the old
# layout (the package dir itself). Once it points at .../home, it is done — so a
# reinstall/upgrade becomes a no-op, and a forced re-run cannot double-migrate.
home_needs_migration() {
    user_exists || return 1
    [ "$(agent_home)" = "$pkgdir" ] || return 1
    [ -d "$pkgdir" ] || [ -e "$staging" ]
}

# True once the directory moves have happened: the package dir holds nothing but
# the migrated home/. Distinguishes a half-finished migration (only the passwd
# update left) from a genuine old-layout home — which always has the skel
# dotfiles useradd copies, so it never looks like "only home/".
home_already_moved() {
    [ -d "$newhome" ] || return 1
    [ -z "$(find "$pkgdir" -mindepth 1 -maxdepth 1 ! -name home -print -quit 2>/dev/null)" ]
}

tmfiles_conf=/usr/lib/tmpfiles.d/opencode-skybox.conf

# Create the package dir, the private home, and the shared coding root, with
# their canonical owner/mode. Requires the account and group to exist already.
create_workspace_dirs() {
    if [ -f "$tmfiles_conf" ]; then
        systemd-tmpfiles --create "$tmfiles_conf"
    else
        echo "  warning: $tmfiles_conf not found — workspace dirs not created" >&2
    fi
}

ensure_password() {
    # /etc/opencode-agent/password is read by systemd as root (EnvironmentFile=);
    # the agent user never needs to read it. Generate once, keep forever
    # (rotation = manual: stop service -> regenerate -> start service).
    if [ ! -f /etc/opencode-agent/password ]; then
        install -d -m 700 -o root -g root /etc/opencode-agent
        (
            umask 077
            printf 'OPENCODE_SERVER_PASSWORD=%s\n' "$(head -c 32 /dev/urandom | base64)" \
                > /etc/opencode-agent/password
        )
        chmod 600 /etc/opencode-agent/password
        echo "  /etc/opencode-agent/password: generated (rotate manually via systemctl stop/start)"
    else
        echo "  /etc/opencode-agent/password: already present, keeping it"
    fi
}

scaffold() {
    # 1. service account. Deliberate deviation from setup_agent_workspace.py's
    #    "never invent the agent user" stance: a package owns its service
    #    account (standard distro practice, cf. postgres/mysql packages).
    if group_exists; then
        echo "  group 'agent-workspace': already exists"
    else
        # -r keeps the shared group in the system GID range (SYS_GID_MIN..MAX,
        # <1000 here), consistent with the system account created below and out
        # of the pool a future human account could be assigned.
        groupadd -r agent-workspace
        echo "  group 'agent-workspace': created"
    fi
    if user_exists; then
        echo "  user 'opencode-agent': already exists"
    else
        # The package dir must exist for `useradd -m -d .../home`; the tmpfiles
        # fragment then sets its final owner/mode once the account exists.
        install -d -m 0750 -o root -g root "$pkgdir"
        useradd -r -m -d "$newhome" -s /usr/bin/nologin opencode-agent
        echo "  user 'opencode-agent': created (system account, home $newhome)"
    fi
    # 2. membership: the agent needs the group so write-sidecar's file carries
    #    it (chgrp-free, via Group= in the unit); humans need it to read the
    #    sidecar's password and traverse the package dir to the coding root.
    if ! id -nG opencode-agent | grep -qw agent-workspace; then
        usermod -aG agent-workspace opencode-agent
        echo "  opencode-agent: added to agent-workspace"
    fi

    ensure_password
}

# Converge the account, dirs and password; reload systemd for the new unit.
setup() {
    scaffold || return 1
    create_workspace_dirs
    systemctl daemon-reload
}

# Upgrade from the old layout: the agent home was the package dir
# (/var/lib/opencode-agent). Move it to .../home, freeing .../coding for the
# disk-backed workspace. Same-subvolume renames under /var/lib: instant. Runs
# before the service starts, and is resumable: a re-run after a partial failure
# resumes from wherever it stopped instead of nesting home/ inside itself.
migrate_home() {
    echo "opencode-skybox: migrating agent home to $newhome"
    systemctl stop opencode-agent 2>/dev/null || true

    if [ -e "$staging" ]; then
        # Interrupted between the two renames: finish moving home/ into place.
        echo "  resuming an interrupted migration ($staging present)"
        if [ -e "$newhome" ]; then
            echo "opencode-skybox: $staging and $newhome both exist — resolve manually" >&2
            return 1
        fi
        install -d -m 0750 -o root -g root "$pkgdir" || return 1
        mv "$staging" "$newhome" || return 1
    elif home_already_moved; then
        # Directory moves done; only the passwd update is left.
        :
    else
        # Fresh old-layout migration (also the state if a previous run stopped
        # before the first rename).
        mv "$pkgdir" "$staging" || return 1
        install -d -m 0750 -o root -g root "$pkgdir" || return 1
        mv "$staging" "$newhome" || return 1
    fi

    if ! usermod -d "$newhome" opencode-agent; then
        echo "opencode-skybox: could not update the passwd home for opencode-agent" >&2
        return 1
    fi
    # Empty leftover from the very old /var/lib/opencode-agent/coding design.
    rmdir "$newhome/coding" 2>/dev/null || true
}

post_install() {
    echo "opencode-skybox: setting up agent server"
    setup || {
        echo "opencode-skybox: setup failed — fix the error above, then run:" >&2
        echo "  sudo pacman -Sf opencode-skybox" >&2
        exit 1
    }

    systemctl enable --now opencode-agent
    echo "opencode-skybox: opencode-agent.service enabled and started"
    echo "  add yourself with:  sudo usermod -aG agent-workspace <user>"
    echo "  (effective in new login sessions) — grants sidecar password read"
    echo "  optional: let the agent open PRs — add the 'skybox' MCP server to"
    echo "  the agent's opencode.json and run 'gh auth login' as yourself;"
    echo "  see README.md 'Capabilities'"
}

post_upgrade() {
    was_active=false
    systemctl is-active --quiet opencode-agent && was_active=true

    migrated=false
    if home_needs_migration; then
        migrate_home || {
            echo "opencode-skybox: home migration failed — fix the error above" >&2
            exit 1
        }
        migrated=true
    fi

    # Converging steps + start (also brings the service back after a migration).
    post_install

    # If it was already running and we did not migrate (which stops it), restart
    # so the freshly installed unit file takes effect. A migration already
    # started a fresh process via enable --now.
    if [ "$was_active" = true ] && [ "$migrated" = false ]; then
        systemctl restart opencode-agent
        echo "opencode-skybox: opencode-agent.service restarted"
    fi
}

pre_remove() {
    if systemctl is-active --quiet opencode-agent 2>/dev/null; then
        systemctl stop opencode-agent
        echo "opencode-skybox: opencode-agent.service stopped"
    fi
    if systemctl is-enabled --quiet opencode-agent 2>/dev/null; then
        systemctl disable opencode-agent
        echo "opencode-skybox: opencode-agent.service disabled"
    fi

    # A guest is not part of the service. Warn while `skybox vm down` still
    # exists; leaving a domain defined is otherwise a virsh cleanup.
    if command -v virsh >/dev/null 2>&1; then
        domains=$(timeout 5 virsh -c qemu:///system list --name 2>/dev/null \
            | grep '^opencode-vm-' || true)
        if [ -n "$domains" ]; then
            echo "opencode-skybox: VM domain(s) still defined or running:" >&2
            printf '%s\n' "$domains" | sed 's/^/    /' >&2
            echo "opencode-skybox: stop them with 'skybox vm down' before removing the package" >&2
        fi
    fi

    systemctl daemon-reload
}

post_remove() {
    # Service account: gone, but the package dir (agent home + coding root) stays.
    if user_exists; then
        userdel opencode-agent
        echo "opencode-skybox: user 'opencode-agent' removed"
        echo "  leftover package dir /var/lib/opencode-agent kept (home: config +"
        echo "  credentials; coding: workspace) — clean up deliberately with:"
        echo "  sudo rm -rf /var/lib/opencode-agent"
    fi

    # VM images/overlays/logs live outside the package dir. Keep them like the
    # agent home and print the deliberate cleanup.
    if [ -d /var/lib/opencode-vm ]; then
        echo "  leftover VM data /var/lib/opencode-vm kept — clean up deliberately with:"
        echo "  sudo rm -rf /var/lib/opencode-vm"
    fi

    # Shared group: only if nobody (a human) is left in it.
    if group_exists; then
        members=$(getent group agent-workspace | cut -d: -f4)
        if [ -z "$members" ]; then
            groupdel agent-workspace
            echo "opencode-skybox: group 'agent-workspace' removed (was memberless)"
        else
            echo "opencode-skybox: group 'agent-workspace' kept — still has members: $members"
        fi
    fi

    # Config + tmpfs runtime state (sidecar / old mount root). The coding root
    # now lives under the package dir, which is kept above.
    rm -rf /etc/opencode-agent
    rm -f /run/opencode-agent/server.json
    rmdir /run/opencode-agent/coding 2>/dev/null || true
    rmdir /run/opencode-agent 2>/dev/null || true

    systemctl daemon-reload
}
