#!/usr/bin/env bash

# The pressure valve.
#
#   panama-hook theme-set dark orchid
#
# Runs ~/.config/panama/hooks/<name> and everything executable in
# ~/.config/panama/hooks/<name>.d/, in sorted order, with the hook's arguments.
#
# This exists so "can Panama also do X when the theme changes" is a five-line
# file somebody drops in a directory rather than a fork, a feature request, or
# a patch that has to be rebased forever. docs/UPSTREAM-INSPIRATION.md defers a
# plugin host as premature and still should: this is the thirty-line version
# that covers most of what people actually want from one, and it has no API to
# keep stable beyond "we will run your script and tell you what happened".
#
# A failing hook is reported and stepped over. Somebody's broken script must
# never break a theme change, an upgrade, or a login -- which is exactly what
# would happen if this used `set -e` and the caller did too.
#
# Hooks run synchronously, so a slow one delays whatever called it. That is
# deliberate: the alternative is a hook whose output arrives after the thing it
# was reacting to has already finished, which is harder to reason about than a
# pause.

set -uo pipefail

HOOK_DIR="${PANAMA_HOOK_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/panama/hooks}"

name="${1:-}"
if [[ -z "$name" ]]; then
    echo 'usage: panama-hook <name> [args...]' >&2
    exit 2
fi
shift

# A hook name reaches the filesystem, so it cannot be allowed to leave the
# directory. Callers are all in-repo today, which is exactly when this is
# cheap to add and easy to forget.
if [[ ! "$name" =~ ^[a-z][a-z0-9-]*$ ]]; then
    echo "panama-hook: refusing hook name: $name" >&2
    exit 2
fi

run_one() {
    local script="$1"
    # Shifted off before the arguments are forwarded, or every hook receives
    # its own path as $1 and the real arguments arrive one place late.
    shift
    [[ -f "$script" && -x "$script" ]] || return 0
    if ! "$script" "$@"; then
        printf 'panama-hook: %s failed (%s); continuing\n' \
            "$(basename "$script")" "$name" >&2
    fi
}

# The single file first, then the .d directory in sorted order. Both are
# optional and having neither is the normal case.
run_one "$HOOK_DIR/$name" "$@"

if [[ -d "$HOOK_DIR/$name.d" ]]; then
    while IFS= read -r script; do
        [[ -n "$script" ]] || continue
        run_one "$script" "$@"
    done < <(find "$HOOK_DIR/$name.d" -maxdepth 1 -type f | sort)
fi

exit 0
