#!/usr/bin/env bash

# The comments in a package list are for the reader, not for dnf.
#
# Every list in setup/packages/ is annotated -- which package exists for which
# settings page, why an exception was made -- and install-packages passed the
# whole file to dnf, comment lines included. dnf does not ignore an argument it
# cannot match; it reports "No match for argument: #" and exits 1. With
# `set -euo pipefail` at the top of that script, the first annotated list ends
# the stage, and a fresh machine gets almost no packages.
#
# It could not show up here. This machine has everything already, so a re-run
# matches every real name and only fails on the comments; and every contract
# that reads these lists strips comments with sed before comparing, so the tests
# were reading a file the installer was not. That is the exact shape of bug this
# repository's fresh-install work exists to find: invisible on the machine it
# was written on, fatal on the next one.
#
# The check runs the installer's own extraction rather than describing it, so
# renaming or deleting the filter fails here instead of silently passing.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
installer="$repo_dir/setup/scripts/install-packages"
lists_dir="$repo_dir/setup/packages"

findings=()
note() { findings+=("$1"); }

# ── The installer's own filter, lifted out and run ───────────────────────────

filter="$(sed -n '/^packages_in()/,/^}/p' "$installer")"
if [[ -z "$filter" ]]; then
    printf 'package lists contract: install-packages defines no packages_in filter, so it reads lists raw\n' >&2
    exit 1
fi
eval "$filter"

work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT

fixture="$work/list"
cat >"$fixture" <<'LIST'
# A whole-line comment, which is what broke the install.
alpha
bravo

#Indented and unspaced forms of the same thing.
   # spaced
charlie
LIST

read -ra extracted <<<"$(packages_in "$fixture")"

expected=(alpha bravo charlie)
if [[ "${extracted[*]}" != "${expected[*]}" ]]; then
    note "a commented list extracts as '${extracted[*]}' rather than '${expected[*]}'"
fi

for token in "${extracted[@]}"; do
    [[ "$token" == *"#"* ]] && note "the extracted list still carries a comment marker: $token"
done

# ── Every real list survives it ──────────────────────────────────────────────
#
# Stripping comments must not also strip packages. Each name that is not a
# comment has to come out the other side, or this fix trades a loud failure for
# a quiet one.

shopt -s nullglob
for list in "$lists_dir"/*; do
    [[ -f "$list" ]] || continue
    name="$(basename "$list")"

    # What a reader would say the file declares.
    mapfile -t declared < <(sed 's/#.*//' "$list" | tr -d ' \t' | grep -v '^$' | sort)
    read -ra passed <<<"$(packages_in "$list")"
    mapfile -t passed_sorted < <(printf '%s\n' "${passed[@]}" | sort)

    if [[ "${declared[*]}" != "${passed_sorted[*]}" ]]; then
        note "$name declares ${#declared[@]} packages but the installer would pass ${#passed_sorted[@]}"
    fi

    for token in "${passed[@]}"; do
        [[ "$token" == *"#"* ]] && note "$name would pass a comment marker to the package manager"
    done
done

# ── No list is read raw any more ─────────────────────────────────────────────
#
# The filter existing is not the same as it being used. A single missed call
# site is a stage that still dies on the list it forgot.

if grep -nE 'tr "\\n" " " *<' "$installer" >/dev/null; then
    note 'a package list is still read with tr rather than through packages_in'
fi

# ── Report ───────────────────────────────────────────────────────────────────

if (( ${#findings[@]} > 0 )); then
    mapfile -t findings < <(printf '%s\n' "${findings[@]}" | sort -u)
    printf 'package lists contract: %d finding(s)\n' "${#findings[@]}" >&2
    printf '  - %s\n' "${findings[@]}" >&2
    exit 1
fi

printf 'package lists contract: PASS\n'
