#!/usr/bin/env bash
# Install rewritten uaccess-only hardware-wallet udev rules from the
# same pinned Sparrow tarball the Flatpak uses. Not a Flatpak hook.
#
# Default: print the plan, then re-exec with sudo if not root.
# --dry-run: verify pin (unless --fixture) and print rewritten rules only.

set -euo pipefail

script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [[ -f "${script_dir}/udev-uaccess.sh" ]]; then
    # Bundled next to this script (Flatpak files/libexec or apex helper dir).
    # shellcheck disable=SC1091
    source "${script_dir}/udev-uaccess.sh"
    repo_root="${script_dir}"
    PINS="${script_dir}/vendor-pins.json"
    default_work="${TMPDIR:-/tmp}/thepeoples-sparrow-udev"
elif [[ -f "${script_dir}/../lib/udev-uaccess.sh" ]]; then
    repo_root="$(cd -- "${script_dir}/../.." && pwd)"
    # shellcheck disable=SC1091
    source "${script_dir}/../lib/udev-uaccess.sh"
    PINS="${repo_root}/catalog/vendor-pins.json"
    default_work="${repo_root}/work"
else
    printf '[ERROR] missing udev-uaccess.sh next to %s or in scripts/lib/\n' "$0" >&2
    exit 1
fi

APP_ID="io.thepeoples.Sparrow"
WORK_DIR="${WORK_DIR:-${default_work}}"
RULES_DIR="${RULES_DIR:-/etc/udev/rules.d}"
DRY_RUN=false
FIXTURE=""
orig_args=("$@")

fail() { printf '[ERROR] %s\n' "$*" >&2; exit 1; }
log() { printf '[INFO] %s\n' "$*"; }
need() { command -v "$1" >/dev/null 2>&1 || fail "missing command: $1"; }

usage() {
    cat <<EOF
Usage: $0 [--dry-run] [--fixture FILE] [--rules-dir DIR]

Extracts Sparrow udev rules from the pinned signed tarball, drops GROUP=
and MODE=, keeps TAG+=\"uaccess\", installs into /etc/udev/rules.d.
No plugdev group. No other files.
EOF
}

while [[ $# -gt 0 ]]; do
    case "$1" in
        --dry-run) DRY_RUN=true ;;
        --fixture) FIXTURE="${2:-}"; shift ;;
        --rules-dir) RULES_DIR="${2:-}"; shift ;;
        --help|-h) usage; exit 0 ;;
        *) fail "unknown argument: $1" ;;
    esac
    shift
done

print_plan() {
    cat <<EOF
Plan:
  1. Vendor-verify ${APP_ID} tarball (catalog/vendor-pins.json)
  2. Extract Sparrow/lib/runtime/conf/udev/*.rules
  3. Rewrite: drop GROUP= and MODE=; keep TAG+=\"uaccess\"
  4. install rewritten rules to ${RULES_DIR}
  5. udevadm control --reload-rules && udevadm trigger
No plugdev. No other paths.
EOF
}

if [[ -n "$FIXTURE" ]]; then
    [[ -f "$FIXTURE" ]] || fail "fixture not found: ${FIXTURE}"
    tmp="$(mktemp)"
    rewrite_udev_uaccess "$FIXTURE" "$tmp"
    cat "$tmp"
    rm -f "$tmp"
    exit 0
fi

need jq
need curl
need gpg
need sha256sum
need tar

print_plan

if [[ "$DRY_RUN" != true && "$(id -u)" -ne 0 ]]; then
    if sudo -n true 2>/dev/null; then
        exec sudo -n -- "$0" "${orig_args[@]}"
    fi
    if sudo -- "$0" "${orig_args[@]}"; then
        exit 0
    fi
    fail "sudo denied; run the plan above by hand"
fi

pin="$(jq -e --arg id "$APP_ID" '.apps[$id]' "$PINS")" || fail "missing pin ${APP_ID}"
filename="$(jq -er '.filename' <<<"$pin")"
url="$(jq -er '.url' <<<"$pin")"
expect_sha="$(jq -er '.sha256' <<<"$pin")"
manifest_url="$(jq -er '.manifest_url' <<<"$pin")"
manifest_sig_url="$(jq -er '.manifest_sig_url' <<<"$pin")"
key_url="$(jq -er '.vendor_key_url' <<<"$pin")"
fingerprint="$(jq -er '.vendor_key_fingerprint' <<<"$pin")"

mkdir -p "$WORK_DIR"
tarball="${WORK_DIR}/${filename}"
manifest="${WORK_DIR}/$(basename "$manifest_url")"
manifest_sig="${WORK_DIR}/$(basename "$manifest_sig_url")"
keyfile="${WORK_DIR}/vendor-key.asc"
[[ -s "$tarball" ]] || curl -L --fail --show-error --output "$tarball" "$url"
[[ -s "$manifest" ]] || curl -L --fail --show-error --output "$manifest" "$manifest_url"
[[ -s "$manifest_sig" ]] || curl -L --fail --show-error --output "$manifest_sig" "$manifest_sig_url"
curl -L --fail --show-error --output "$keyfile" "$key_url"

gnupg_home="$(mktemp -d)"
chmod 700 "$gnupg_home"
gpg --homedir "$gnupg_home" --batch --import "$keyfile" >/dev/null 2>&1
gpg --homedir "$gnupg_home" --batch --verify "$manifest_sig" "$manifest"
actual="$(sha256sum "$tarball" | awk '{print $1}')"
[[ "$actual" == "$expect_sha" ]] || fail "tarball sha256 mismatch"
rm -rf "$gnupg_home"
log "verified ${filename} ${actual}"

extract="$(mktemp -d)"
tar -xzf "$tarball" -C "$extract" --wildcards 'Sparrow/lib/runtime/conf/udev/*.rules'
shopt -s nullglob
rules=("$extract"/Sparrow/lib/runtime/conf/udev/*.rules)
shopt -u nullglob
[[ ${#rules[@]} -gt 0 ]] || fail "tarball has no udev rules"

if [[ "$DRY_RUN" == true ]]; then
    for rule in "${rules[@]}"; do
        log "rewritten $(basename "$rule"):"
        rewrite_udev_uaccess "$rule" /dev/stdout
    done
    rm -rf "$extract"
    exit 0
fi

need udevadm
install -d "$RULES_DIR"
for rule in "${rules[@]}"; do
    dest="${RULES_DIR}/$(basename "$rule")"
    tmp="$(mktemp)"
    rewrite_udev_uaccess "$rule" "$tmp"
    install -m 0644 "$tmp" "$dest"
    rm -f "$tmp"
    log "installed ${dest}"
done
udevadm control --reload-rules
udevadm trigger
rm -rf "$extract"
log "udev rules reloaded"
