#!/usr/bin/env bash
# The fleet payload's settings.json must name only paths a box actually has: workstation
# locations are rewritten to their box-side location, and anything left unaccounted for
# aborts staging instead of shipping a hook Claude Code would skip without a word.
set -uo pipefail

MOD="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
root="$(mktemp -d)"
trap 'rm -rf "$root"' EXIT

fail=0
t() { # name expected actual
  if [ "$2" = "$3" ]; then printf 'ok    %s\n' "$1"
  else printf 'FAIL  %s\n      expected: %s\n      actual:   %s\n' "$1" "$2" "$3"; fail=$((fail + 1)); fi
}
has() { # name needle haystack
  case "$3" in *"$2"*) printf 'ok    %s\n' "$1";;
    *) printf 'FAIL  %s\n      wanted substring: %s\n      actual: %s\n' "$1" "$2" "$3"; fail=$((fail + 1));; esac
}

repo="$root/repo"
source_home="$root/workstation"
box_home="$root/box"
target_home="$box_home/.claude"
devtools="$root/devtools.json"
deploy="$root/deploy/modules/workstation/claude"
mkdir -p "$repo" "$deploy/hooks" "$deploy/bin" "$source_home/Projects/slopgate/hooks" "$box_home"
printf '#!/bin/sh\n' >"$deploy/hooks/present.sh"
printf '#!/bin/sh\n' >"$deploy/bin/bun"
chmod 755 "$deploy/hooks/present.sh" "$deploy/bin/bun"

# hooks and bin are `source: deploy` in the real manifest — the executed bytes come from
# the landed deploy clone, not the working tree.
cat >"$root/manifest.json" <<'JSON'
{"entries":[{"path":"hooks","source":"deploy"},{"path":"bin","source":"deploy"},{"path":"settings.json","source":"repo"}]}
JSON

cat >"$devtools" <<'JSON'
{
  "root": "~/.dev-tools",
  "hook_paths": {
    "rewrites": [
      { "from": "/opt/fixture/bun", "to": "{claude_home}/bin/bun" },
      { "from": "~/Projects/slopgate", "to": "{devtools_root}/slopgate" }
    ]
  },
  "tools": [ { "name": "slopgate" } ]
}
JSON

write_settings() { # command
  jq -n --arg command "$1" '{hooks:{PreToolUse:[{hooks:[{type:"command",command:$command}]}]}}' >"$repo/settings.json"
}

with_env() { # command...
  MOD="$MOD" CLAUDE_MANIFEST="$root/manifest.json" CLAUDE_REPO_ROOT="$repo" \
    OVERDECK_DEPLOY_DIR="$root/deploy" CLAUDE_HOME_TARGET="$target_home" \
    CLAUDE_SOURCE_HOME="$source_home" CLAUDE_DEVTOOLS="$devtools" "$@"
}

staged_command() { # -> the single hook command as staged, or nothing on failure
  with_env bash -c '
    . "$1/lib/claude-home.sh"
    stage=$(claude_home_stage) || exit 1
    jq -r ".hooks.PreToolUse[0].hooks[0].command" "$stage/tree/settings.json"
    rm -rf "$stage"' _ "$MOD"
}

stage_error() { # -> stderr of a staging run expected to fail
  with_env bash -c '
    . "$1/lib/claude-home.sh"
    stage=$(claude_home_stage) && { echo "STAGED-ANYWAY"; rm -rf "$stage"; }' _ "$MOD" 2>&1
}

write_settings "bash $target_home/hooks/present.sh"
t "payload hook survives untouched" \
  "bash $target_home/hooks/present.sh" "$(staged_command)"

write_settings "/opt/fixture/bun $target_home/hooks/present.sh"
t "workstation interpreter is rewritten to the payload wrapper" \
  "$target_home/bin/bun $target_home/hooks/present.sh" "$(staged_command)"

# The regression this file exists for: these hooks are installed on every box by
# devtools.json. Dropping the registration disables the gate fleet-wide, silently.
write_settings "$source_home/Projects/slopgate/hooks/commit-hook.sh"
t "workstation tool checkout is rewritten, never dropped" \
  "$box_home/.dev-tools/slopgate/hooks/commit-hook.sh" "$(staged_command)"

write_settings "bash $target_home/hooks/missing.sh"
has "hook missing from the payload aborts staging" \
  "settings hook target missing from payload: $target_home/hooks/missing.sh" "$(stage_error)"

write_settings "$source_home/.fewtok/hooks/read-gate.sh"
has "path with no box-side equivalent aborts staging" \
  "which is neither payload state under $target_home nor a declared devtools path" "$(stage_error)"

write_settings "$box_home/.dev-tools/undeclared/hooks/x.sh"
has "devtools path for an undeclared tool aborts staging" \
  "devtools.json declares no tool 'undeclared'" "$(stage_error)"

# The shared settings.json the fleet actually ships, against the real devtools map. No
# assertion here names this machine: what is checked is that nothing survives preparation
# which a box could not resolve.
real="$root/real-settings.json"
cp "$MOD/../workstation/claude/settings.json" "$real"
unresolved="$(
  MOD="$MOD" bash -c '
    . "$1/lib/claude-home.sh"
    claude_home_prepare_settings "$2" >/dev/null || exit 1
    devtools_root="$(claude_home_devtools_root)"
    claude_home_settings_commands "$2" | while IFS= read -r cmd; do
      read -r -a toks <<<"$cmd"
      for tok in "${toks[@]}"; do
        bare="${tok#[\"\x27]}"; bare="${bare%[\"\x27]}"
        case "$bare" in
          "$CLAUDE_HOME_TARGET"/* | "$devtools_root"/*) ;;
          /*) printf "%s\n" "$bare" ;;
        esac
      done
    done' _ "$MOD" "$real"
)"
t "shipped settings.json resolves entirely to fleet paths" "" "$unresolved"

[ "$fail" = 0 ] || { printf '\n%d failed\n' "$fail"; exit 1; }
printf '\nall passed\n'
