#!/usr/bin/env bash
# The collector takes ~35-55s to rebind, so deploy-local.sh must restart it only when a
# path feeding the running process changed since the last fully-successful deploy — and
# must fail open (restart) in every ambiguous direction: a wrong skip serves stale code.
set -uo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SCRIPT="$ROOT/packaging/deploy-local.sh"
fails=0
check() { if [ "$1" = 0 ]; then printf 'ok - %s\n' "$2"; else printf 'FAIL - %s\n' "$2"; fails=$((fails + 1)); fi; }

bash -n "$SCRIPT"
check $? "deploy-local.sh parses"

# Structure: the grouped release command excludes the independently activated collector,
# and the component receipt base is selected before its gate and immutable activation.
grep -q 'release_restart_cmd="systemctl --user restart ${release_restart_services\[\*\]}"' "$SCRIPT"
check $? "grouped release restart command uses only its gated service list"

base_line=$(grep -n 'collector_base=$(component_convergence_base collector' "$SCRIPT" | head -1 | cut -d: -f1)
gate_line=$(grep -n 'collector_unchanged_since_stamp "$collector_base" "$deployment_sha"' "$SCRIPT" | head -1 | cut -d: -f1)
if [ -n "$base_line" ] && [ -n "$gate_line" ] && [ "$base_line" -lt "$gate_line" ]; then rc=0; else rc=1; fi
check "$rc" "the gate uses the collector convergence base and deployed sha"

activate_line=$(grep -n 'backend-release.sh" activate collector' "$SCRIPT" | head -1 | cut -d: -f1)
if [ -n "$gate_line" ] && [ -n "$activate_line" ] && [ "$gate_line" -lt "$activate_line" ]; then rc=0; else rc=1; fi
check "$rc" "the gate runs before immutable collector activation"

stage_calls=$(grep -c 'LOCAL_GATE_ACTIVE=1 bash .*stage-backend-release.sh" collector' "$SCRIPT")
if [[ "$stage_calls" == 2 ]]; then rc=0; else rc=1; fi
check "$rc" "collector artifacts materialize on the activation host instead of a remote mirror"

# Functional: extract the gate (paths array + function) and run it against a fixture repo.
TESTROOT="${XDG_CACHE_HOME:-$HOME/.cache}/overdeck/tests/collector-restart-gate/run-$$-$RANDOM"
mkdir -p "$TESTROOT"
trap 'chmod -R u+w "$TESTROOT" 2>/dev/null; rm -rf "$TESTROOT"' EXIT

GATE_SRC="$TESTROOT/gate.sh"
{
  sed -n '/^COLLECTOR_RESTART_PATHS=(/,/^)/p' "$SCRIPT"
  sed -n '/^collector_unchanged_since_stamp() (/,/^)/p' "$SCRIPT"
} >"$GATE_SRC"
grep -q 'collector_unchanged_since_stamp() (' "$GATE_SRC" && grep -q 'COLLECTOR_RESTART_PATHS=(' "$GATE_SRC"
check $? "gate function and path list extract cleanly"

FIXTURE="$TESTROOT/repo"
git init -q "$FIXTURE"
git -C "$FIXTURE" -c user.email=t@t -c user.name=t commit -q --allow-empty -m init
mkdir -p "$FIXTURE/collector/src" "$FIXTURE/docs" \
  "$FIXTURE/packages/activity-contract/src" \
  "$FIXTURE/packages/report-contract/src" \
  "$FIXTURE/modules/workstation/claude/hooks/lib"
echo v1 >"$FIXTURE/collector/src/index.ts"
echo v1 >"$FIXTURE/packages/activity-contract/src/index.ts"
echo v1 >"$FIXTURE/packages/report-contract/src/index.ts"
echo v1 >"$FIXTURE/modules/workstation/claude/hooks/lib/decision-marker.mjs"
git -C "$FIXTURE" add -A && git -C "$FIXTURE" -c user.email=t@t -c user.name=t commit -q -m base
BASE=$(git -C "$FIXTURE" rev-parse HEAD)
echo doc >"$FIXTURE/docs/note.md"
git -C "$FIXTURE" add -A && git -C "$FIXTURE" -c user.email=t@t -c user.name=t commit -q -m docs
DOCS_ONLY=$(git -C "$FIXTURE" rev-parse HEAD)
echo v2 >"$FIXTURE/collector/src/index.ts"
git -C "$FIXTURE" add -A && git -C "$FIXTURE" -c user.email=t@t -c user.name=t commit -q -m touch-collector
COLLECTOR_TOUCHED=$(git -C "$FIXTURE" rev-parse HEAD)
git -C "$FIXTURE" reset -q --hard "$DOCS_ONLY"
echo v2 >"$FIXTURE/modules/workstation/claude/hooks/lib/decision-marker.mjs"
git -C "$FIXTURE" add -A && git -C "$FIXTURE" -c user.email=t@t -c user.name=t commit -q -m touch-detector
DETECTOR_TOUCHED=$(git -C "$FIXTURE" rev-parse HEAD)
git -C "$FIXTURE" reset -q --hard "$DOCS_ONLY"
echo v2 >"$FIXTURE/packages/activity-contract/src/index.ts"
git -C "$FIXTURE" add -A && git -C "$FIXTURE" -c user.email=t@t -c user.name=t commit -q -m touch-contract
CONTRACT_TOUCHED=$(git -C "$FIXTURE" rev-parse HEAD)
git -C "$FIXTURE" reset -q --hard "$DOCS_ONLY"
echo v2 >"$FIXTURE/packages/report-contract/src/index.ts"
git -C "$FIXTURE" add -A && git -C "$FIXTURE" -c user.email=t@t -c user.name=t commit -q -m touch-report-contract
REPORT_CONTRACT_TOUCHED=$(git -C "$FIXTURE" rev-parse HEAD)

# The gate calls systemctl --user is-active; fake it per-case via a function the
# subshell inherits. FAKE_ACTIVE_RC=0 → active, else inactive.
run_gate() {
  local stamp=$1 target=$2 active_rc=$3
  (
    export DEPLOY="$FIXTURE"
    FAKE_ACTIVE_RC="$active_rc"
    systemctl() { return "$FAKE_ACTIVE_RC"; }
    # shellcheck disable=SC1090
    source "$GATE_SRC"
    collector_unchanged_since_stamp "$stamp" "$target"
  )
}

run_gate "$BASE" "$DOCS_ONLY" 0
check $? "docs-only delta with an active collector skips the restart"

! run_gate "$BASE" "$COLLECTOR_TOUCHED" 0
check $? "a collector/ delta forces the restart"

! run_gate "$BASE" "$DETECTOR_TOUCHED" 0
check $? "a shared decision detector delta forces the restart"

! run_gate "$BASE" "$CONTRACT_TOUCHED" 0
check $? "the activity contract delta forces the restart"

! run_gate "$BASE" "$REPORT_CONTRACT_TOUCHED" 0
check $? "the report contract delta forces the restart"

! run_gate "$BASE" "$DOCS_ONLY" 3
check $? "an inactive collector forces the restart even with no delta"

! run_gate "" "$DOCS_ONLY" 0
check $? "an absent stamp forces the restart"

! run_gate "not-a-sha" "$DOCS_ONLY" 0
check $? "a malformed stamp forces the restart"

! run_gate "$(printf 'a%.0s' {1..40})" "$DOCS_ONLY" 0
check $? "an unknown base commit forces the restart"

[ "$fails" -eq 0 ] || { printf '%s check(s) failed\n' "$fails" >&2; exit 1; }
printf 'collector-restart-gate: all checks passed\n'
