#!/usr/bin/env bash
# YachtMemory v2 fault/restart test. Explicit opt-in, YMServer, isolated test DB ONLY.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
DB=yachtmemory_catalog_v2_test
VESSEL=e4b76f55-6bd9-4a2e-ab5a-8c538d9311fd
R="$ROOT/receiver/ym-catalog-v2-fault-test-receiver.php"

[[ $(id -un) != root ]] || { echo 'ABORT: launch as normal YMServer operator; sudo only for mysql/php'; exit 1; }
[[ -f "$R" ]] || exit 1
grep -Fxq "const DB_NAME = 'yachtmemory_catalog_v2_test';" "$R" || { echo 'ABORT: wrong target'; exit 1; }
bash -n "$0"; php -l "$R"
[[ "$(sudo mysql -N -e "SELECT COUNT(*) FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='$DB'")" == 1 ]] || { echo 'ABORT: isolated DB absent'; exit 1; }
# Test vessel must not exist. Existing Wildbad fixture and test rows are preserved.
[[ "$(sudo mysql -N "$DB" -e "SELECT COUNT(*) FROM telemetry_vessels WHERE vessel_id=UUID_TO_BIN('$VESSEL')")" == 0 ]] || { echo 'ABORT: dedicated fault-test vessel exists; one-shot test'; exit 1; }
# Crucial: no reference to any productive schema in SQL writes.
sudo mysql "$DB" -e "INSERT INTO telemetry_vessels(vessel_id,vessel_key,created_at_utc) VALUES(UUID_TO_BIN('$VESSEL'),'catalog-v2-fault-test',UTC_TIMESTAMP(3))"
q() { sudo mysql -N "$DB" -e "$1"; }
assert_sql() { local actual; actual="$(q "$1")"; if [[ "$actual" != "$2" ]]; then echo "FAIL expected [$2] actual [$actual]: $1"; exit 1; fi; }
run() { sudo -n env -u YM_CATALOG_V2_TEST_FAULT /usr/bin/php "$R" < "$1"; }
expect() {
  local file=$1 wanted=$2 result
  result="$(run "$file")"
  printf '%s: %s\n' "$file" "$result"
  python3 - "$wanted" "$result" <<'PYEX'
import json,sys
j=json.loads(sys.argv[2]); assert j.get('ok') is True and j.get('status')==sys.argv[1],j
PYEX
}
stage() {
 local rev=$1 d="fixtures/revision-$1" f
 expect "$d/header.json" staged
 while IFS= read -r f; do expect "$f" staged; done < <(find "$d" -maxdepth 1 -name 'chunk-*.json' | sort -r)
}
active() { assert_sql "SELECT revision FROM signalk_catalog_active_v2 WHERE vessel_id=UUID_TO_BIN('$VESSEL')" "$1"; }
observed() { assert_sql "SELECT COUNT(*) FROM signalk_catalog_paths_v2 WHERE vessel_id=UUID_TO_BIN('$VESSEL') AND currently_observed=1" "$1"; }
history() { assert_sql "SELECT COUNT(*) FROM signalk_catalog_paths_v2 WHERE vessel_id=UUID_TO_BIN('$VESSEL')" "$1"; }

printf '\n=== A. Baseline activation and retry ===\n'
stage 1
expect fixtures/revision-1/finalize.json activated
active 1; observed 1;history 1
expect fixtures/revision-1/finalize.json already_active
expect fixtures/revision-1/chunk-000.json already_staged

printf '\n=== B. Exception between writes and COMMIT; automatic rollback ===\n'
stage 2
set +e
ERROR="$(sudo -n env YM_CATALOG_V2_TEST_FAULT=throw-before-commit /usr/bin/php "$R" < fixtures/revision-2/finalize.json 2>&1)"
RC=$?
set -e
printf '%s\n' "$ERROR"
[[ $RC -ne 0 && "$ERROR" == *INJECTED_FAULT_BEFORE_COMMIT* ]] || { echo 'FAIL: exception injection'; exit 1; }
active 1; observed 1; history 1
assert_sql "SELECT status FROM signalk_catalog_snapshots_v2 WHERE snapshot_id=UUID_TO_BIN('c3bb73f2-52af-45c4-a8f9-ccddac3cbf28')" staging
expect fixtures/revision-2/finalize.json activated
active 2;observed 2;history 2
expect fixtures/revision-2/finalize.json already_active

printf '\n=== C. SIGKILL immediately before COMMIT; connection rollback ===\n'
stage 3
# Confirm supported POSIX function before invoking kill injection.
if php -r 'exit(function_exists("posix_kill") ? 0 : 1);'; then
 set +e
 ERROR="$(sudo -n env YM_CATALOG_V2_TEST_FAULT=kill-before-commit /usr/bin/php "$R" < fixtures/revision-3/finalize.json 2>&1)"
 RC=$?
 set -e
 printf '%s\n' "$ERROR"
 [[ $RC -ne 0 && "$ERROR" == *INJECTED_KILL_BEFORE_COMMIT* ]] || { echo 'FAIL: SIGKILL injection'; exit 1; }
 active 2;observed 2;history 2
 assert_sql "SELECT status FROM signalk_catalog_snapshots_v2 WHERE snapshot_id=UUID_TO_BIN('d85d0cea-5775-4d7a-9afa-630f80ec49d3')" staging
 expect fixtures/revision-3/finalize.json activated
 active 3;observed 3;history 3
else
 echo 'UNTESTED: php-posix missing; SIGKILL case skipped, NOT counted as pass'
 exit 2
fi

printf '\n=== D. Later revision and delayed older revision ===\n'
stage 4
expect fixtures/revision-4/finalize.json activated
active 4;observed 4;history 4
expect fixtures/revision-4/finalize.json already_active
expect fixtures/revision-3/finalize.json superseded
printf '\n=== E. Pending older rev5 arrives after rev6 activation ===\n'
stage 5
stage 6
expect fixtures/revision-6/finalize.json activated
active 6; observed 5; history 5
expect fixtures/revision-5/finalize.json superseded
active 6; observed 5; history 5
expect fixtures/revision-5/finalize.json superseded
assert_sql "SELECT COUNT(*) FROM signalk_catalog_snapshots_v2 WHERE vessel_id=UUID_TO_BIN('$VESSEL') AND status='active'" 1
assert_sql "SELECT COUNT(*) FROM signalk_catalog_paths_v2 WHERE vessel_id=UUID_TO_BIN('$VESSEL') AND first_seen_utc>last_seen_utc" 0
printf '\n=== FAULT/RESTART MYSQL TEST PASS ===\n'
