# packages.sh — apt, Homebrew, and snap package installation.
# shellcheck shell=bash
# Requires: _validate_packages, _validated_pkgs, warn (from helpers.sh).
# Must run sequentially — _validated_pkgs is a shared global; parallelization would race.
# Copyright (c) 2026 Progralink. Licensed under the DevCage License — see LICENSE.

# _install_apt_packages: install APT_PACKAGES if set.
# Runs as root before privilege drop, so full apt-get is available.
_install_apt_packages() {
    if [ "${APT_PACKAGES:-}" = "" ]; then return; fi
    # Validate package names to prevent shell injection via crafted values.
    # Only allow valid Debian package name characters (letters, digits,
    # hyphens, dots, plus signs) with optional arch qualifiers (e.g. pkg:amd64).
    # shellcheck disable=SC2154  # _validated_pkgs populated by _validate_packages in helpers.sh
    _validate_packages "$APT_PACKAGES" '^[a-zA-Z0-9][a-zA-Z0-9.+-]*(:[a-zA-Z0-9]+)?$' "apt"
    # shellcheck disable=SC2154  # _validated_pkgs set by _validate_packages
    echo "entrypoint: installing apt packages: ${_validated_pkgs[*]}" >&2
    apt-get update -qq && \
        apt-get install -y --no-install-recommends "${_validated_pkgs[@]}" && \
        rm -rf /var/lib/apt/lists/*
}

# _install_brew_packages: install BREW_PACKAGES if set.
# Runs as devcage user via su (brew must not run as root).
_install_brew_packages() {
    if [ "${BREW_PACKAGES:-}" = "" ]; then return; fi
    # Validate Homebrew formula names (letters, digits, hyphens, underscores,
    # slashes for taps like user/tap/formula).
    _validate_packages "$BREW_PACKAGES" '^[a-zA-Z0-9][a-zA-Z0-9_./-]*$' "brew"
    echo "entrypoint: installing brew packages: ${BREW_PACKAGES}" >&2
    # Pass each validated package as a separate argument via printf to avoid
    # shell injection through su -c string interpolation.
    printf '%s\0' "${_validated_pkgs[@]}" | \
        su devcage -s /bin/bash -c 'xargs -0 /home/linuxbrew/.linuxbrew/bin/brew install' \
        || warn "brew install failed for some packages — continuing"
}

# _install_snap_packages: install SNAP_PACKAGES if set.
# Runs as root before privilege drop. snapd is installed on demand (not in the
# image) because most sessions don't need it. Uses --devmode since Docker
# containers lack AppArmor/seccomp support for strict snap confinement.
_install_snap_packages() {
    if [ "${SNAP_PACKAGES:-}" = "" ]; then return; fi
    # Validate snap names: lowercase letters, digits, hyphens only.
    _validate_packages "$SNAP_PACKAGES" '^[a-z][a-z0-9-]*$' "snap"
    echo "entrypoint: installing snapd and snap packages: ${SNAP_PACKAGES}" >&2

    # Step 1: Install snapd via apt (on-demand, not in image)
    if apt-get update -qq && \
       apt-get install -y --no-install-recommends snapd && \
       rm -rf /var/lib/apt/lists/*; then

        # Step 2: Create required directories (normally done by systemd units)
        mkdir -p /run/snapd /var/snap /snap

        # Step 3: Start snapd daemon in background (no systemd in Docker)
        /usr/lib/snapd/snapd &
        local _snapd_pid=$!

        # Step 4: Wait for snapd to become ready (up to 30 seconds)
        local _snap_ready=0 _i
        for ((_i = 1; _i <= 30; _i++)); do
            if snap version >/dev/null 2>&1; then
                _snap_ready=1
                break
            fi
            sleep 1
        done

        if [ "$_snap_ready" = "1" ]; then
            # Step 5: Install each snap with --devmode
            local _snap_pkg
            for _snap_pkg in "${_validated_pkgs[@]}"; do
                echo "entrypoint: snap install --devmode ${_snap_pkg}" >&2
                snap install --devmode "$_snap_pkg" \
                    || warn "snap install failed for '${_snap_pkg}' — continuing"
            done

            # Step 6: Ensure /snap/bin is in PATH for snap binaries
            export PATH="/snap/bin:${PATH}"

            # Stop snapd daemon — no longer needed after installs
            kill "$_snapd_pid" 2>/dev/null || true
            wait "$_snapd_pid" 2>/dev/null || true
        else
            warn "snapd did not become ready within 30s — skipping snap installs"
            kill "$_snapd_pid" 2>/dev/null || true
            wait "$_snapd_pid" 2>/dev/null || true
            pkill -f snapd 2>/dev/null || true
        fi
    else
        warn "failed to install snapd via apt — skipping snap packages"
    fi
}
