#!/usr/bin/env bash
set -euo pipefail

# LOAD-07 controls only the exact Timetabler Phase 1 publisher. Resource
# Booking owns its independently synchronized consumer restart. Durable outbox
# state is never deleted or edited, and application dotenv is never sourced.

APP_DIR=${TT_PHASE1_APP_DIR:?TT_PHASE1_APP_DIR is required}
RUN_ID=${TT_PHASE1_LOAD_RUN_ID:?TT_PHASE1_LOAD_RUN_ID is required}
EXPECTED_SHA=${TT_PHASE1_LOAD_EXPECTED_SHA:?TT_PHASE1_LOAD_EXPECTED_SHA is required}
COORDINATION_SHA256=${TT_PHASE1_LOAD_COORDINATION_SHA256:?coordination SHA-256 is required}
EXECUTE_AT_EPOCH=${TT_PHASE1_LOAD_EXECUTE_AT_EPOCH:?execute-at epoch is required}
PUBLISHER_NAME=${TT_PHASE1_PUBLISHER_NAME:-timetabler-rb-phase1-publisher}
PYTHON_BIN=${TT_PHASE1_PYTHON_BIN:-"$APP_DIR/venv/bin/python"}

[[ "$RUN_ID" =~ ^phase1-load-[0-9]{8}t[0-9]{6}z-[0-9a-f]{12,32}$ ]]
[[ "$EXPECTED_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$COORDINATION_SHA256" =~ ^[0-9a-f]{64}$ ]]
[[ "$EXECUTE_AT_EPOCH" =~ ^[0-9]{10}$ ]]
[[ "${TT_PHASE1_LOAD_CONFIRM_MUTATION:-False}" == "True" ]]
[[ "${TT_PHASE1_LOAD_CONFIRM_OPERATIONAL:-False}" == "True" ]]
[[ "${RB_INTEGRATION_REVERSE_DELIVERY_ENABLED:-False}" == "False" ]]
[[ "${RB_INTEGRATION_PHASE:-phase1}" == "phase1" ]]
[[ -x "$PYTHON_BIN" ]]
[[ "$(git -C "$APP_DIR" rev-parse HEAD)" == "$EXPECTED_SHA" ]]

# shellcheck source=deploy/resolve_phase1_pm2.sh
source "$APP_DIR/deploy/resolve_phase1_pm2.sh"
phase1_resolve_pm2
PM2_BIN=$PHASE1_PM2_BIN

count_process() {
  PM2_SILENT=true "$PM2_BIN" jlist --silent | \
    "$PYTHON_BIN" "$APP_DIR/deploy/parse_pm2_jlist.py" count --name "$PUBLISHER_NAME"
}

readiness() {
  "$PYTHON_BIN" "$APP_DIR/manage.py" \
    preflight_resource_booking_phase1 --require-enabled
}

extract_field() {
  "$PYTHON_BIN" -c \
    'import json,sys; print(json.load(sys.stdin)[sys.argv[1]])' "$1"
}

[[ "$(count_process "$PUBLISHER_NAME")" == "1" ]]
before_json=$(readiness)
before_watermark=$(printf '%s' "$before_json" | extract_field transport_watermark)
before_published=$(printf '%s' "$before_json" | extract_field publisher_last_sequence)
[[ "$before_watermark" == "$before_published" ]]

# The Python driver waits locally for the shared fence. Re-attest the clock at
# the process boundary and refuse to touch PM2 if this host is early or more
# than five seconds late.
timing_json=$("$PYTHON_BIN" -c \
  'import json,sys,time; target=int(sys.argv[1]); actual=time.time(); skew=actual-target; assert 0 <= skew <= 5, "restart fence skew outside 0..5 seconds"; print(json.dumps({"actual_execute_at_epoch":actual,"execute_at_skew_seconds":round(skew,6)},sort_keys=True))' \
  "$EXECUTE_AT_EPOCH")
actual_execute_at_epoch=$(printf '%s' "$timing_json" | extract_field actual_execute_at_epoch)
execute_at_skew_seconds=$(printf '%s' "$timing_json" | extract_field execute_at_skew_seconds)

fail_closed() {
  status=$?
  trap - EXIT
  if [[ "$status" -ne 0 ]]; then
    "$PM2_BIN" stop "$PUBLISHER_NAME" >/dev/null 2>&1 || true
    printf '{"operation":"failed_closed","publisher_running":false,"durable_state_deleted":false,"resource_booking_process_touched":false}\n' >&2
  fi
  exit "$status"
}
trap fail_closed EXIT

cd "$APP_DIR"
"$PM2_BIN" restart "$PUBLISHER_NAME" >/dev/null
"$PYTHON_BIN" manage.py probe_resource_booking_publisher --liveness >/dev/null
[[ "$(count_process "$PUBLISHER_NAME")" == "1" ]]
"$PM2_BIN" save --force >/dev/null

after_json=$(readiness)
after_watermark=$(printf '%s' "$after_json" | extract_field transport_watermark)
after_published=$(printf '%s' "$after_json" | extract_field publisher_last_sequence)
[[ "$after_watermark" == "$after_published" ]]
trap - EXIT

printf '{"operation":"publisher_restart","run_id":"%s","execute_at_epoch":%s,"actual_execute_at_epoch":%s,"execute_at_skew_seconds":%s,"coordination_sha256":"%s","entry_watermark":%s,"final_watermark":%s,"publisher_last_sequence":%s,"publisher_count":1,"publisher_restart_count":1,"publisher_liveness":true,"durable_state_deleted":false,"reverse_delivery_enabled":false,"resource_booking_process_touched":false}\n' \
  "$RUN_ID" "$EXECUTE_AT_EPOCH" "$actual_execute_at_epoch" "$execute_at_skew_seconds" "$COORDINATION_SHA256" \
  "$before_watermark" "$after_watermark" "$after_published"
