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

APP_DIR=${TT_ENGINE_RESPONSE_APP_DIR:?TT_ENGINE_RESPONSE_APP_DIR is required}
APP_DIR=$(cd "$APP_DIR" && pwd -P)
CONSUMER_NAME=${TT_ENGINE_RESPONSE_CONSUMER_NAME:-tt_response}
VIRTUAL_ENV_DIR="$APP_DIR/venv"
VIRTUAL_ENV_BIN="$VIRTUAL_ENV_DIR/bin"
PYTHON_BIN="$VIRTUAL_ENV_BIN/python"
CONSUMER_SCRIPT="$APP_DIR/kafka_consumer/tt_response.py"
ECOSYSTEM_FILE="$APP_DIR/deploy/tt_response.ecosystem.config.cjs"
PARSER="$APP_DIR/deploy/parse_pm2_jlist.py"
PREFLIGHT="$APP_DIR/deploy/preflight_tt_response_runtime.py"
PROC_ROOT=${TT_ENGINE_RESPONSE_PROC_ROOT:-/proc}
STABILITY_SECONDS=${TT_ENGINE_RESPONSE_STABILITY_SECONDS:-20}

case "$STABILITY_SECONDS" in
  ""|*[!0-9]*) echo "tt_response stability interval must be an integer" >&2; exit 2 ;;
esac
if (( STABILITY_SECONDS > 60 )); then
  echo "tt_response stability interval must not exceed 60 seconds" >&2
  exit 2
fi

if [[ ! -x "$PYTHON_BIN" ]]; then
  echo "tt_response virtualenv Python is not executable" >&2
  exit 1
fi
for required_path in "$CONSUMER_SCRIPT" "$ECOSYSTEM_FILE" "$PARSER" "$PREFLIGHT"; do
  if [[ ! -f "$required_path" ]]; then
    echo "tt_response deployment input is missing: $required_path" >&2
    exit 1
  fi
done
if [[ ! -d "$PROC_ROOT" ]] || [[ "$PROC_ROOT" != /* ]]; then
  echo "tt_response process filesystem root is invalid" >&2
  exit 1
fi

# Validate the selected venv, compile the worker, and import its external
# dependencies without initializing Django apps or opening DB/Redis clients.
(
  cd "$APP_DIR"
  PYTHONDONTWRITEBYTECODE=1 DJANGO_SETTINGS_MODULE=backend.settings \
    "$PYTHON_BIN" "$PREFLIGHT" \
      --app-dir "$APP_DIR" \
      --virtual-env "$VIRTUAL_ENV_DIR"
)

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

export TT_ENGINE_RESPONSE_APP_DIR="$APP_DIR"
export TT_ENGINE_RESPONSE_CONSUMER_NAME="$CONSUMER_NAME"
export VIRTUAL_ENV="$VIRTUAL_ENV_DIR"
original_path=${PATH:-/usr/bin:/bin}
PATH=$VIRTUAL_ENV_BIN
IFS=: read -r -a original_path_entries <<< "$original_path"
for path_entry in "${original_path_entries[@]}"; do
  if [[ -n "$path_entry" ]] && [[ "$path_entry" != "$VIRTUAL_ENV_BIN" ]]; then
    PATH="$PATH:$path_entry"
  fi
done
export PATH

pm2_jlist() {
  PM2_SILENT=true "$PM2_BIN" jlist --silent
}

attest_basic() {
  pm2_jlist | "$PYTHON_BIN" "$PARSER" attest-script \
    --name "$CONSUMER_NAME" \
    --script-path "$CONSUMER_SCRIPT" \
    --print-state
}

attest_runtime() {
  pm2_jlist | "$PYTHON_BIN" "$PARSER" attest-script \
    --name "$CONSUMER_NAME" \
    --script-path "$CONSUMER_SCRIPT" \
    --interpreter-path "$PYTHON_BIN" \
    --cwd "$APP_DIR" \
    --virtual-env "$VIRTUAL_ENV_DIR" \
    --path-prefix "$VIRTUAL_ENV_BIN" \
    --proc-root "$PROC_ROOT" \
    "$@" \
    --print-state
}

other_process_fingerprint() {
  pm2_jlist | "$PYTHON_BIN" "$PARSER" fingerprint-excluding \
    --name "$CONSUMER_NAME"
}

pre_state=$(attest_basic)
IFS=: read -r pre_pm_id _pre_pid pre_restart_count <<< "$pre_state"
pre_other_fingerprint=$(other_process_fingerprint)

"$PM2_BIN" startOrRestart "$ECOSYSTEM_FILE" \
  --only "$CONSUMER_NAME" --update-env >/dev/null

expected_restart_count=$((pre_restart_count + 1))
post_state=$(attest_runtime \
  --expected-pm-id "$pre_pm_id" \
  --expected-restart-count "$expected_restart_count")
IFS=: read -r post_pm_id post_pid post_restart_count <<< "$post_state"

sleep "$STABILITY_SECONDS"
attest_runtime \
  --expected-pm-id "$post_pm_id" \
  --expected-pid "$post_pid" \
  --expected-restart-count "$post_restart_count" >/dev/null

post_other_fingerprint=$(other_process_fingerprint)
if [[ "$pre_other_fingerprint" != "$post_other_fingerprint" ]]; then
  echo "a non-tt_response PM2 process changed during the guarded restart" >&2
  exit 1
fi

"$PM2_BIN" save --force >/dev/null
echo "tt_response restarted once with the existing Timetabler virtualenv and remained stable"
