#!/usr/bin/env bash
# brew-preinstall — content-addressed install/remove of OS-managed Homebrew packages.
#
# Triggered by hash change across preinstall.d/*.Brewfile, not a manual version bump.
# Handles the full package lifecycle: new packages added by OS updates are installed,
# packages removed from Brewfiles are uninstalled — without touching user-added packages.
#
# State: ~/.local/share/ublue-os/brew-preinstall-state.json
#   { "hash": "<sha256 of all Brewfiles>", "packages": ["pkg1", "pkg2", ...] }
set -euo pipefail

PREINSTALL_DIR="/usr/share/ublue-os/homebrew/preinstall.d"
STATE_FILE="${HOME}/.local/share/ublue-os/brew-preinstall-state.json"
BREW_BIN="/var/home/linuxbrew/.linuxbrew/bin/brew"

if [[ ! -x "${BREW_BIN}" ]]; then
    echo "brew-preinstall: brew not found at ${BREW_BIN}, skipping"
    exit 0
fi

if [[ ! -d "${PREINSTALL_DIR}" ]]; then
    echo "brew-preinstall: no preinstall.d at ${PREINSTALL_DIR}, skipping"
    exit 0
fi

shopt -s nullglob
brewfiles=("${PREINSTALL_DIR}"/*.Brewfile)

if [[ ${#brewfiles[@]} -eq 0 ]]; then
    echo "brew-preinstall: no Brewfiles in ${PREINSTALL_DIR}, skipping"
    exit 0
fi

# Content-addressed check: hash all Brewfiles combined.
# If unchanged, nothing to do — fast exit without touching brew.
current_hash=$(cat "${brewfiles[@]}" | sha256sum | cut -d' ' -f1)
stored_hash=$(jq -r '.hash // ""' "${STATE_FILE}" 2>/dev/null || echo "")

if [[ "${current_hash}" == "${stored_hash}" ]]; then
    echo "brew-preinstall: Brewfiles unchanged (${current_hash:0:12}...), nothing to do"
    exit 0
fi

eval "$("${BREW_BIN}" shellenv)"

echo "brew-preinstall: Brewfiles changed (${current_hash:0:12}...), applying..."

# Install all packages declared in Brewfiles (brew bundle is idempotent)
for brewfile in "${brewfiles[@]}"; do
    echo "brew-preinstall: bundling ${brewfile}"
    brew bundle --file="${brewfile}"
done

# Uninstall packages removed from our managed set (OS diet).
# Parse the package names we manage now vs what we managed before,
# then remove the delta — but only if brew still has them installed.
# This never touches packages the user added outside our managed set.
current_packages=$(grep -h '^brew "' "${brewfiles[@]}" \
    | sed 's/brew "\([^"]*\)".*/\1/' | sort -u)
previous_packages=$(jq -r '.packages[] // empty' "${STATE_FILE}" 2>/dev/null \
    | sort -u || true)

if [[ -n "${previous_packages}" ]]; then
    removed=$(comm -23 \
        <(echo "${previous_packages}") \
        <(echo "${current_packages}") || true)
    for pkg in ${removed}; do
        if brew list --formula "${pkg}" &>/dev/null; then
            echo "brew-preinstall: removing ${pkg} (dropped from managed set)"
            brew uninstall "${pkg}" --ignore-dependencies 2>/dev/null \
                || echo "brew-preinstall: warning: could not remove ${pkg}, skipping"
        fi
    done
fi

# Persist new state: hash + full managed package list for next diff.
# Write atomically via temp file + rename to avoid corrupt state on SIGKILL.
mkdir -p "$(dirname "${STATE_FILE}")"
packages_json=$(echo "${current_packages}" | jq -R . | jq -s .)
jq -n \
    --arg hash "${current_hash}" \
    --argjson packages "${packages_json}" \
    '{"hash": $hash, "packages": $packages}' > "${STATE_FILE}.tmp"
mv -f "${STATE_FILE}.tmp" "${STATE_FILE}"

echo "brew-preinstall: complete"
