#!/bin/bash

# omarchy:hidden=true
# omarchy:summary=Create an approval request for a blocked USB device

set -euo pipefail

case "${USBGUARD_IPC_SIGNAL:-}" in
IPC.Connected) ;;
Device.PolicyApplied)
  # PresenceChanged precedes policy evaluation, even for trusted devices.
  [[ ${USBGUARD_DEVICE_TARGET_NEW:-} == "block" ]] || exit 0
  ;;
Device.PresenceChanged)
  [[ ${USBGUARD_DEVICE_EVENT:-} == "Remove" ]] || exit 0
  ;;
*) exit 0 ;;
esac

requests="$HOME/.local/state/omarchy/usb-authorization/requests"
mkdir -p "$requests"
chmod 700 "${requests%/*}" "$requests"
exec 9>"$requests/.lock"

request_generation() {
  local daemon_id

  # USBGuard reuses device IDs after restarting. Include both the daemon
  # invocation and watcher lifetime so old prompts cannot suppress new ones.
  daemon_id=$(systemctl show usbguard.service --property=InvocationID --value) || return 1
  [[ $daemon_id =~ ^[0-9a-f]{32}$ ]] || return 1
  printf '%s:%s' "${OMARCHY_USB_AUTHORIZATION_WATCH_ID:-standalone}" "$daemon_id"
}

if [[ $USBGUARD_IPC_SIGNAL == "IPC.Connected" ]]; then
  flock 9
  generation=$(request_generation) || exit 1
  for pending in "$requests"/request-*.json; do
    [[ -f $pending ]] || continue
    if ! jq -e --arg generation "$generation" '.generation == $generation' "$pending" >/dev/null 2>&1; then
      rm -f "$pending"
    fi
  done
  flock -u 9

  # USBGuard scans present devices before accepting IPC connections. Repeat
  # this on every reconnect: temporary approvals disappear on daemon restart.
  blocked=$(usbguard list-devices --blocked) || exit 1
  while IFS= read -r line; do
    if [[ $line =~ ^([0-9]+):[[:space:]](block.*)$ ]]; then
      USBGUARD_IPC_SIGNAL=Device.PolicyApplied \
        USBGUARD_DEVICE_ID="${BASH_REMATCH[1]}" \
        USBGUARD_DEVICE_TARGET_NEW=block \
        USBGUARD_DEVICE_RULE="${BASH_REMATCH[2]}" \
        "$OMARCHY_PATH/bin/omarchy-usb-authorization-event" || true
    fi
  done <<<"$blocked"
  exit 0
fi

[[ ${USBGUARD_DEVICE_ID:-} =~ ^[0-9]+$ ]] || exit 0
[[ -n ${USBGUARD_DEVICE_RULE:-} ]] || exit 0

# A removed device's final target may be allow even though a blocked request
# remains. Its identity attributes are unchanged; normalize the target.
USBGUARD_DEVICE_RULE="block ${USBGUARD_DEVICE_RULE#* }"

# The token is safe to carry through the presentation launcher's command
# string. The device-controlled rule itself stays in a mode-0600 JSON file and
# is never interpolated into a shell command.
token="request-$(printf '%s\0%s' "$USBGUARD_DEVICE_ID" "$USBGUARD_DEVICE_RULE" | sha256sum | cut -d' ' -f1)"
request="$requests/$token.json"
# watch executes handlers concurrently. A reconnect scan and a policy event
# can refer to the same device; publish only one pending request.
if [[ $USBGUARD_IPC_SIGNAL == "Device.PresenceChanged" ]]; then
  flock 9
  blocked=$(usbguard list-devices --blocked) || exit 1
  # A delayed removal event must not erase a request for a reappeared device.
  if ! grep -Fqx -- "$USBGUARD_DEVICE_ID: $USBGUARD_DEVICE_RULE" <<<"$blocked"; then
    rm -f "$request"
  fi
  exit 0
fi

# Keep retrying without needing another USB event. Handlers belong to the
# watcher service and are stopped with the graphical session.
while true; do
  flock 9
  generation=$(request_generation) || exit 1
  if [[ -f $request ]] && jq -e --arg generation "$generation" '.generation == $generation' "$request" >/dev/null 2>&1; then
    exit 0
  fi

  # Discard events overtaken by another policy decision or device replacement.
  blocked=$(usbguard list-devices --blocked) || exit 1
  current=0
  while IFS= read -r line; do
    if [[ $line == "$USBGUARD_DEVICE_ID: $USBGUARD_DEVICE_RULE" ]]; then
      current=1
      break
    fi
  done <<<"$blocked"
  (( current )) || exit 0

  # Login and shell restarts can leave D-Bus alive before notifications are ready.
  # Bound each wait so disappeared devices are rechecked between attempts.
  if ! omarchy-notification-wait 1; then
    flock -u 9
    sleep 1
    continue
  fi

  temporary=$(mktemp "$requests/.request.XXXXXXXXXX")
  trap 'rm -f "$temporary"' EXIT

  jq -n \
    --arg id "$USBGUARD_DEVICE_ID" \
    --arg rule "$USBGUARD_DEVICE_RULE" \
    --arg generation "$generation" \
    '{id: $id, rule: $rule, generation: $generation}' >"$temporary"
  chmod 600 "$temporary"
  mv -f "$temporary" "$request"

  if omarchy-notification-send \
    --app-name omarchy-action \
    --urgency critical \
    --glyph 󰖀 \
    "USB accessory blocked" \
    "Click to review it. Keep it blocked unless you recognize the device." \
    --exec omarchy-launch-floating-terminal-with-presentation \
    omarchy-usb-authorization-review "$token"; then
    exit 0
  fi
  rm -f "$request"
  flock -u 9
  sleep 1
done
