#!/usr/bin/env bash

# One IpcHandler per target name.
#
# `qs ipc call <target> ...` resolves by name, and the handlers are spread
# across a shell.qml several hundred lines long -- a second IpcHandler with an
# already-used target is exactly the mistake a reader makes there, and it does
# not error: one handler silently shadows or conflicts with the other. For the
# polkit target that is not cosmetic -- the agent delivers authentication
# requests through it, so a shadowed `begin` breaks every password prompt on
# the desktop. This nearly shipped once; now it cannot.
#
# Harness files are excluded: each is its own root, never loaded beside the
# shell, so sharing a target with it is not a conflict.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
shell_dir="$repo_dir/config/dot/quickshell"

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

# target name <TAB> file, one line per IpcHandler declaration. The awk pairs
# each `IpcHandler {` with the next quoted `target:` line in the same file,
# which is how every handler in this codebase is written.
declarations="$(find "$shell_dir" -name '*.qml' -not -name '*-harness.qml' -exec awk '
    /IpcHandler *{/ { pending = 1 }
    pending && /target: "/ {
        match($0, /target: "[^"]+"/)
        printf "%s\t%s\n", substr($0, RSTART + 9, RLENGTH - 10), FILENAME
        pending = 0
    }
' {} +)"

# The scan finding almost nothing means the declaration format changed, not
# that the handlers went away -- fail loudly rather than pass on an empty read.
total="$(grep -c . <<<"$declarations" || true)"
(( total >= 5 )) \
    || note "only $total IpcHandler targets were found; the scan no longer matches how handlers are declared"

while read -r count target; do
    [[ -n "$target" ]] || continue
    if (( count > 1 )); then
        files="$(awk -F'\t' -v t="$target" '$1 == t { print $2 }' <<<"$declarations" \
            | sed "s|$repo_dir/||" | paste -sd ', ')"
        note "the \"$target\" IPC target is declared $count times ($files); one silently shadows the other"
    fi
done < <(cut -f1 <<<"$declarations" | sort | uniq -c)

if (( ${#findings[@]} > 0 )); then
    printf 'ipc targets contract: %d finding(s)\n' "${#findings[@]}" >&2
    printf '  - %s\n' "${findings[@]}" >&2
    exit 1
fi

printf 'ipc targets contract: PASS (%d targets, each declared once)\n' "$total"
