#!/bin/sh
set -u
umask 077

CONF="/etc/router-egress-emergency-refresh.conf"
HOOK_INIT="/etc/init.d/router-egress-emergency-decision"
WATCHER_INIT="/etc/init.d/router-egress-health-repair"

FAILED_BACKUP="/root/step050m07c4-backup-20260711-203956-6486"
FAILED_ROLLBACK="/root/rollback-step050m07c4-20260711-203956-6486.sh"

EXPECTED_TABLE200_HASH="a03a7a5738c031c280e4a3147e9f7c5feb4af524f4d7ea427871b3b6cafd4691"
MIN_FREE_KB=131072

fact() {
  printf '__FACT__ %s=%s\n' "$1" "$2"
}

block() {
  echo "__BLOCK_BEGIN__ $1"
  printf '%s\n' "$2"
  echo "__BLOCK_END__ $1"
}

bool_cmd() {
  if "$@" >/dev/null 2>&1; then
    printf true
  else
    printf false
  fi
}

table200_hash() {
  ip route show table 200 2>/dev/null |
    sort |
    sha256sum |
    sed 's/[[:space:]].*$//'
}

strict_iface() {
  interface="$1"
  attempt=1

  while [ "$attempt" -le 3 ]; do
    if ping \
      -I "$interface" \
      -c 1 \
      -W 3 \
      1.1.1.1 \
      >/dev/null 2>&1
    then
      return 0
    fi

    attempt=$((attempt + 1))
    sleep 1
  done

  return 1
}

strict_all() {
  for interface in vpn1 vpn2 vpn3 vpn4 vpn5; do
    strict_iface "$interface" ||
      return 1
  done

  return 0
}

routes_all() {
  for table in 201 202 203 204 205; do
    ip route show table "$table" 2>/dev/null |
      grep -q '^default ' ||
      return 1
  done

  return 0
}

free_kb() {
  df -Pk / |
    awk 'NR == 2 {print $4}'
}

echo "__TRACE__ stage=before"

DF_BEFORE="$(
  df -Pk / 2>&1 ||
  true
)"

block df_before "$DF_BEFORE"

DU_BEFORE="$(
  {
    du -sk \
      /root/step050m07* \
      /var/lib/router-egress-recovery/step050m07* \
      2>/dev/null ||
    true
  } |
  sort -n
)"

block candidate_usage_before "$DU_BEFORE"

TABLE200_BEFORE="$(
  ip route show table 200 2>&1 ||
  true
)"

block table200_before "$TABLE200_BEFORE"

fact table200_hash_before "$(table200_hash)"
fact hook_running_before "$(bool_cmd "$HOOK_INIT" running)"
fact hook_enabled_before "$(bool_cmd "$HOOK_INIT" enabled)"
fact watcher_running_before "$(bool_cmd "$WATCHER_INIT" running)"
fact watcher_enabled_before "$(bool_cmd "$WATCHER_INIT" enabled)"

CONFIG_RAW_BEFORE="$(
  (
    . "$CONF"
    printf '%s' "${EMERGENCY_COMMIT_ENABLED:-UNSET}"
  )
)"

fact commit_raw_before "$CONFIG_RAW_BEFORE"

PROCESSES_BEFORE="$(
  ps w 2>/dev/null |
    grep -E \
      'hmn-refresh-pool-safe|router-egress-emergency-refresh|rebalance-top5' |
    grep -v grep ||
  true
)"

block relevant_processes_before "$PROCESSES_BEFORE"

if [ -n "$PROCESSES_BEFORE" ]; then
  echo "__ERROR__ refresh_or_rebalance_process_present"
  exit 21
fi

fact emergency_lock_before "$(
  bool_cmd test -e /var/lock/router-egress-emergency-refresh.lock
)"

fact refresh_lock_before "$(
  bool_cmd test -e /tmp/hmn-refresh-pool-safe.lock
)"

echo "__TRACE__ stage=remove_incomplete_c4_artifacts"

FAILED_BACKUP_SIZE_KB=0

if [ -e "$FAILED_BACKUP" ]; then
  FAILED_BACKUP_SIZE_KB="$(
    du -sk "$FAILED_BACKUP" 2>/dev/null |
      awk '{print $1}' ||
    echo 0
  )"

  rm -rf -- "$FAILED_BACKUP"
fi

rm -f -- "$FAILED_ROLLBACK"

# Удаляются только временные файлы конкретного неуспешного C4.
rm -f \
  /etc/router-egress-emergency-refresh.conf.step050m07c4.*.tmp \
  /tmp/step050m07c4-* \
  2>/dev/null ||
true

fact incomplete_c4_backup_size_kb "$FAILED_BACKUP_SIZE_KB"
fact incomplete_c4_backup_removed "$(
  if [ -e "$FAILED_BACKUP" ]; then
    echo false
  else
    echo true
  fi
)"

echo "__TRACE__ stage=restore_table200"

# До C4 table 200 не содержала default route.
# Удаляем только exact temporary default, установленный C4.
while ip route show table 200 2>/dev/null |
  grep -q '^default dev vpn1\([[:space:]]\|$\)'
do
  ip route del table 200 default dev vpn1 ||
    break
done

TABLE200_AFTER_ROUTE="$(
  ip route show table 200 2>&1 ||
  true
)"

block table200_after_route_restore "$TABLE200_AFTER_ROUTE"

TABLE200_HASH_AFTER="$(table200_hash)"
fact table200_hash_after "$TABLE200_HASH_AFTER"

if [ "$TABLE200_HASH_AFTER" != "$EXPECTED_TABLE200_HASH" ]; then
  echo "__ERROR__ table200_hash_mismatch"
  echo "__EXPECTED__ table200_hash=$EXPECTED_TABLE200_HASH"
  echo "__OBSERVED__ table200_hash=$TABLE200_HASH_AFTER"
  exit 22
fi

echo "__TRACE__ stage=restore_services"

"$HOOK_INIT" running >/dev/null 2>&1 ||
  "$HOOK_INIT" start >/dev/null 2>&1 ||
{
  echo "__ERROR__ hook_start_failed"
  exit 23
}

"$WATCHER_INIT" running >/dev/null 2>&1 ||
  "$WATCHER_INIT" start >/dev/null 2>&1 ||
{
  echo "__ERROR__ watcher_start_failed"
  exit 24
}

sleep 2

HOOK_RUNNING="$(bool_cmd "$HOOK_INIT" running)"
HOOK_ENABLED="$(bool_cmd "$HOOK_INIT" enabled)"
WATCHER_RUNNING="$(bool_cmd "$WATCHER_INIT" running)"
WATCHER_ENABLED="$(bool_cmd "$WATCHER_INIT" enabled)"

fact hook_running_after "$HOOK_RUNNING"
fact hook_enabled_after "$HOOK_ENABLED"
fact watcher_running_after "$WATCHER_RUNNING"
fact watcher_enabled_after "$WATCHER_ENABLED"

[ "$HOOK_RUNNING" = true ] || {
  echo "__ERROR__ hook_not_running"
  exit 25
}

[ "$WATCHER_RUNNING" = true ] || {
  echo "__ERROR__ watcher_not_running"
  exit 26
}

echo "__TRACE__ stage=verify_baseline"

STRICT_ALL="$(bool_cmd strict_all)"
ROUTES_ALL="$(bool_cmd routes_all)"

fact strict_all "$STRICT_ALL"
fact routes_all "$ROUTES_ALL"

[ "$STRICT_ALL" = true ] || {
  echo "__ERROR__ strict_not_all"
  exit 27
}

[ "$ROUTES_ALL" = true ] || {
  echo "__ERROR__ routes_not_all"
  exit 28
}

CONFIG_RAW_AFTER="$(
  (
    . "$CONF"
    printf '%s' "${EMERGENCY_COMMIT_ENABLED:-UNSET}"
  )
)"

fact commit_raw_after "$CONFIG_RAW_AFTER"

[ "$CONFIG_RAW_AFTER" = "1" ] || {
  echo "__ERROR__ config_raw_unexpected"
  echo "__EXPECTED__ commit_raw=1"
  echo "__OBSERVED__ commit_raw=$CONFIG_RAW_AFTER"
  exit 29
}

fact emergency_lock_after "$(
  bool_cmd test -e /var/lock/router-egress-emergency-refresh.lock
)"

fact refresh_lock_after "$(
  bool_cmd test -e /tmp/hmn-refresh-pool-safe.lock
)"

[ ! -e /var/lock/router-egress-emergency-refresh.lock ] || {
  echo "__ERROR__ emergency_lock_present"
  exit 30
}

[ ! -e /tmp/hmn-refresh-pool-safe.lock ] || {
  echo "__ERROR__ refresh_lock_present"
  exit 31
}

FREE_KB_AFTER="$(free_kb)"
fact free_kb_after "$FREE_KB_AFTER"
fact minimum_free_kb "$MIN_FREE_KB"

case "$FREE_KB_AFTER" in
  ''|*[!0-9]*)
    echo "__ERROR__ free_space_parse_failed"
    exit 32
    ;;
esac

if [ "$FREE_KB_AFTER" -lt "$MIN_FREE_KB" ]; then
  echo "__ERROR__ free_space_below_minimum"
  echo "__EXPECTED__ free_kb_at_least=$MIN_FREE_KB"
  echo "__OBSERVED__ free_kb=$FREE_KB_AFTER"
  exit 33
fi

DF_AFTER="$(
  df -Pk / 2>&1 ||
  true
)"

block df_after "$DF_AFTER"

DU_AFTER="$(
  {
    du -sk \
      /root/step050m07* \
      /var/lib/router-egress-recovery/step050m07* \
      2>/dev/null ||
    true
  } |
  sort -n
)"

block candidate_usage_after "$DU_AFTER"

fact refresh_ran false
fact rebalance_apply_ran false
fact direct_failopen_changed false
fact plan_changed false

echo "__TRACE__ stage=complete"
