#!/bin/sh
set -u
umask 077

RUNNER="/usr/local/sbin/router-egress-emergency-refresh.sh"
PLANNER="/usr/local/sbin/router-egress-hmn-plan-top5.sh"
HELPER="/usr/local/lib/router-egress-recovery-state.sh"
CONF="/etc/router-egress-emergency-refresh.conf"
POOL="/root/hmn/cache/ok-awg1-strict-foreign-latest.tsv"

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

EXPECTED_CRON='/root/hmn/hmn-refresh-pool-safe.sh'

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

block() {
  name="$1"
  shift

  echo "__BLOCK_BEGIN__ $name"
  "$@" 2>&1 || true
  echo "__BLOCK_END__ $name"
}

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

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
}

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
}

state_value() {
  key="$1"
  fallback="$2"

  (
    unset REG_STATE_DIR
    . "$HELPER"
    reg_get_state "$key" "$fallback"
  )
}

repair_counter() {
  (
    unset REG_STATE_DIR
    . "$HELPER"
    reg_daily_repair_get
  )
}

for required in \
  "$RUNNER" \
  "$PLANNER" \
  "$HELPER" \
  "$CONF" \
  "$POOL"
do
  [ -f "$required" ] || {
    echo "__ERROR__ source_missing=$required"
    exit 21
  }
done

echo "__TRACE__ stage=clock"

SNAPSHOT_EPOCH="$(date +%s)"

fact snapshot_epoch "$SNAPSHOT_EPOCH"
fact vm101_local_time "$(date '+%Y-%m-%dT%H:%M:%S%z')"
fact vm101_utc_time "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"

fact etc_tz "$(
  tr '\r\n ' '_' < /etc/TZ 2>/dev/null ||
  echo UNAVAILABLE
)"

fact uci_timezone "$(
  uci -q get system.@system[0].timezone 2>/dev/null ||
  echo UNAVAILABLE
)"

echo "__TRACE__ stage=schedule"

CRON_LINE="$(
  grep -E \
    '^[[:space:]]*20[[:space:]]+4[[:space:]]+\*[[:space:]]+\*[[:space:]]+\*[[:space:]]+' \
    /etc/crontabs/root \
    2>/dev/null |
  grep -F "$EXPECTED_CRON" |
  head -n1 ||
  true
)"

[ -n "$CRON_LINE" ] || {
  echo "__ERROR__ schedule_missing=20_4_daily_hmn_refresh"
  exit 22
}

fact cron_0420_found true
fact cron_line_sha256 "$(
  printf '%s\n' "$CRON_LINE" |
    sha256sum |
    sed 's/[[:space:]].*$//'
)"

echo "__BLOCK_BEGIN__ exact_cron_line"
printf '%s\n' "$CRON_LINE"
echo "__BLOCK_END__ exact_cron_line"

echo "__TRACE__ stage=endpoints"

HEALTHY_COUNT=0
ENDPOINT_COUNT=0

for interface in vpn1 vpn2 vpn3 vpn4 vpn5; do
  if strict_iface "$interface"; then
    strict=true
    HEALTHY_COUNT=$((HEALTHY_COUNT + 1))
  else
    strict=false
  fi

  fact "strict.${interface}" "$strict"

  ENDPOINT_LINE="$(
    wg show "$interface" endpoints 2>/dev/null |
      head -n1 ||
    true
  )"

  ENDPOINT="$(
    printf '%s\n' "$ENDPOINT_LINE" |
      cut -f2 |
      tr -d '\r\n'
  )"

  if [ -z "$ENDPOINT" ] ||
     [ "$ENDPOINT" = "(none)" ]
  then
    echo "__ERROR__ endpoint_unresolved=$interface"
    exit 23
  fi

  ENDPOINT_COUNT=$((ENDPOINT_COUNT + 1))

  fact "endpoint.${interface}" "$ENDPOINT"

  DUMP_LINE="$(
    wg show "$interface" dump 2>/dev/null |
      sed -n '2p'
  )"

  PUBLIC_KEY="$(
    printf '%s\n' "$DUMP_LINE" |
      cut -f1
  )"

  HANDSHAKE="$(
    printf '%s\n' "$DUMP_LINE" |
      cut -f5
  )"

  RX_BYTES="$(
    printf '%s\n' "$DUMP_LINE" |
      cut -f6
  )"

  TX_BYTES="$(
    printf '%s\n' "$DUMP_LINE" |
      cut -f7
  )"

  fact "public_key_sha256.${interface}" "$(
    printf '%s' "$PUBLIC_KEY" |
      sha256sum |
      sed 's/[[:space:]].*$//'
  )"

  fact "latest_handshake.${interface}" "${HANDSHAKE:-0}"
  fact "rx_bytes.${interface}" "${RX_BYTES:-0}"
  fact "tx_bytes.${interface}" "${TX_BYTES:-0}"
done

fact endpoint_count "$ENDPOINT_COUNT"
fact healthy_vpn_slots "$HEALTHY_COUNT"
fact strict_all "$(
  if [ "$HEALTHY_COUNT" -eq 5 ]; then
    echo true
  else
    echo false
  fi
)"

fact routes_201_205 "$(bool_cmd routes_all)"

echo "__TRACE__ stage=runtime"

fact hook_running "$(bool_cmd "$HOOK_INIT" running)"
fact hook_enabled "$(bool_cmd "$HOOK_INIT" enabled)"
fact watcher_running "$(bool_cmd "$WATCHER_INIT" running)"
fact watcher_enabled "$(bool_cmd "$WATCHER_INIT" enabled)"

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

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

fact state_mode "$(state_value mode UNKNOWN)"
fact state_status "$(
  state_value last_emergency_refresh_status UNKNOWN
)"
fact state_epoch "$(
  state_value last_emergency_refresh_epoch 0
)"
fact repair_counter "$(repair_counter)"

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

fact pool_sha256 "$(
  sha256sum "$POOL" |
    sed 's/[[:space:]].*$//'
)"

fact pool_rows "$(
  sed '1d' "$POOL" |
    grep -c . ||
  true
)"

fact pool_mtime_epoch "$(date -r "$POOL" +%s)"

LATEST_GENERATION="$(
  readlink -f /root/hmn/configs/awg1/latest 2>/dev/null ||
  true
)"

[ -n "$LATEST_GENERATION" ] ||
  LATEST_GENERATION="UNRESOLVED"

fact latest_generation "$LATEST_GENERATION"

block runner_dry_run "$RUNNER" --dry-run
block current_planner "$PLANNER"

block routes sh -c '
  for table in 200 201 202 203 204 205; do
    echo "===== table $table ====="
    ip route show table "$table" 2>/dev/null || true
  done
'

block cron_log_tail sh -c '
  tail -n 300 \
    /root/hmn/logs/hmn-refresh-pool-cron.log \
    2>/dev/null ||
  true
'

block emergency_log_tail sh -c '
  tail -n 300 \
    /var/log/router-egress-emergency-refresh.log \
    2>/dev/null ||
  true
'

fact read_only true
fact refresh_ran false
fact rebalance_ran false
fact network_changed false
fact services_changed false
fact state_changed false
fact timer_changed false
fact plan_changed false
fact direct_failopen_changed false

echo "__TRACE__ stage=complete"

[ "$ENDPOINT_COUNT" -eq 5 ] || exit 31
[ "$HEALTHY_COUNT" -eq 5 ] || exit 32
[ "$(bool_cmd routes_all)" = true ] || exit 33

exit 0
