#!/usr/bin/env bash

set -Eeuo pipefail
umask 077
export LANG=C

EXPECTED_HOSTNAME="sqjcnginx01"
EXPECTED_IPV4="10.2.203.102"
RUN_ID=${1:-}
EVIDENCE_DIR="/var/backups/zhct-q041/evidence/${RUN_ID}"
TMP_ROOT=
NGINX_PID=
HAPROXY_PID=
SENTINEL5_PID=
SENTINEL7_PID=
VIP_RE='10\.2\.203\.(115|116|117)'
TEMP_PORTS='18081|18443|26390|26391'
TARGET_PORTS='8081|8443|1883|8083|3306|3308|6379|6387|26379|26387|3000|10008'
UNITS=(nginx.service keepalived.service haproxy.service orchestrator.service sentinel5.service sentinel7.service)

die() {
  printf 'ERROR: %s\n' "$*" >&2
  exit 1
}

stop_pid() {
  local pid=${1:-}
  [[ -n "${pid}" ]] || return 0
  if kill -0 "${pid}" >/dev/null 2>&1; then
    kill "${pid}" >/dev/null 2>&1 || true
    for _ in {1..20}; do
      kill -0 "${pid}" >/dev/null 2>&1 || break
      sleep 0.25
    done
    kill -KILL "${pid}" >/dev/null 2>&1 || true
  fi
  wait "${pid}" >/dev/null 2>&1 || true
}

cleanup() {
  stop_pid "${SENTINEL7_PID}"
  stop_pid "${SENTINEL5_PID}"
  stop_pid "${HAPROXY_PID}"
  stop_pid "${NGINX_PID}"
  SENTINEL7_PID=
  SENTINEL5_PID=
  HAPROXY_PID=
  NGINX_PID=
  [[ -z "${TMP_ROOT}" ]] || rm -rf "${TMP_ROOT}"
}

wait_http() {
  local url=$1 expected=$2
  for _ in {1..40}; do
    response=$(curl --fail --silent --max-time 1 "${url}" 2>/dev/null || true)
    [[ "${response}" == "${expected}" ]] && return 0
    sleep 0.25
  done
  return 1
}

wait_redis_ping() {
  local cli=$1 port=$2
  for _ in {1..40}; do
    response=$("${cli}" -h 127.0.0.1 -p "${port}" ping 2>/dev/null || true)
    [[ "${response}" == "PONG" ]] && return 0
    sleep 0.25
  done
  return 1
}

[[ "${EUID}" -eq 0 ]] || die "must run as root"
[[ "${RUN_ID}" =~ ^q041-[0-9]{8}T[0-9]{6}[+]0800$ ]] || die "invalid Q041 run id"
[[ "$(hostname -s)" == "${EXPECTED_HOSTNAME}" ]] || die "hostname mismatch"
ip -o -4 addr show scope global | awk '{print $4}' | cut -d/ -f1 | \
  grep -Fxq "${EXPECTED_IPV4}" || die "expected IPv4 is not configured"
grep -Fq 'postinstall_static_check=PASS' \
  "${EVIDENCE_DIR}/postinstall-static-result.env" || die "postinstall static gate is not PASS"

if ss -lntupH | grep -Eq ":(${TEMP_PORTS}|${TARGET_PORTS})([[:space:]]|$)"; then
  die "a temporary or target port is already listening"
fi
if ip -o -4 addr show | grep -Eq "${VIP_RE}"; then
  die "a project VIP is already announced"
fi

TMP_ROOT=$(mktemp -d /var/tmp/zhct-q041-health.XXXXXX)
trap cleanup EXIT
install -d -m 0700 "${TMP_ROOT}/nginx" "${TMP_ROOT}/sentinel5" "${TMP_ROOT}/sentinel7"

cat >"${TMP_ROOT}/nginx.conf" <<EOF
worker_processes 1;
pid ${TMP_ROOT}/nginx.pid;
error_log ${TMP_ROOT}/nginx-error.log notice;
events { worker_connections 32; }
http {
    access_log off;
    server {
        listen 127.0.0.1:18081;
        location / { return 200 "Q041_NGINX_OK\\n"; }
    }
}
EOF
/usr/local/nginx/sbin/nginx -t -p "${TMP_ROOT}/nginx/" -c "${TMP_ROOT}/nginx.conf" \
  >"${EVIDENCE_DIR}/nginx-temp-config-test.txt" 2>&1
/usr/local/nginx/sbin/nginx -p "${TMP_ROOT}/nginx/" -c "${TMP_ROOT}/nginx.conf" \
  -g 'daemon off;' >"${TMP_ROOT}/nginx-stdout.log" 2>&1 &
NGINX_PID=$!
wait_http "http://127.0.0.1:18081/" "Q041_NGINX_OK" || die "Nginx temporary health check failed"

cat >"${TMP_ROOT}/haproxy.cfg" <<'EOF'
global
    log stdout format raw local0
    maxconn 16

defaults
    log global
    mode http
    timeout connect 2s
    timeout client 5s
    timeout server 5s

frontend q041_health
    bind 127.0.0.1:18443
    http-request return status 200 content-type text/plain string Q041_HAPROXY_OK
EOF
/usr/local/haproxy/sbin/haproxy -c -f "${TMP_ROOT}/haproxy.cfg" \
  >"${EVIDENCE_DIR}/haproxy-temp-config-test.txt" 2>&1
/usr/local/haproxy/sbin/haproxy -db -f "${TMP_ROOT}/haproxy.cfg" \
  >"${TMP_ROOT}/haproxy-stdout.log" 2>&1 &
HAPROXY_PID=$!
wait_http "http://127.0.0.1:18443/" "Q041_HAPROXY_OK" || die "HAProxy temporary health check failed"

cat >"${TMP_ROOT}/sentinel5.conf" <<EOF
port 26390
bind 127.0.0.1
protected-mode yes
daemonize no
dir ${TMP_ROOT}/sentinel5
logfile ""
sentinel monitor q041-redis5 127.0.0.1 6399 1
sentinel down-after-milliseconds q041-redis5 30000
sentinel failover-timeout q041-redis5 180000
sentinel parallel-syncs q041-redis5 1
EOF
/opt/redis/5.0.14/bin/redis-sentinel "${TMP_ROOT}/sentinel5.conf" \
  >"${TMP_ROOT}/sentinel5-stdout.log" 2>&1 &
SENTINEL5_PID=$!
wait_redis_ping /opt/redis/5.0.14/bin/redis-cli 26390 || die "Redis5 Sentinel health check failed"

cat >"${TMP_ROOT}/sentinel7.conf" <<EOF
port 26391
bind 127.0.0.1
protected-mode yes
daemonize no
dir ${TMP_ROOT}/sentinel7
logfile ""
sentinel monitor q041-redis7 127.0.0.1 6398 1
sentinel down-after-milliseconds q041-redis7 30000
sentinel failover-timeout q041-redis7 180000
sentinel parallel-syncs q041-redis7 1
EOF
/opt/redis/7.2.14/bin/redis-sentinel "${TMP_ROOT}/sentinel7.conf" \
  >"${TMP_ROOT}/sentinel7-stdout.log" 2>&1 &
SENTINEL7_PID=$!
wait_redis_ping /opt/redis/7.2.14/bin/redis-cli 26391 || die "Redis7 Sentinel health check failed"

node_interface=$(ip -o -4 addr show scope global | awk -v expected="${EXPECTED_IPV4}" '
  {
    split($4, address, "/")
    if (address[1] == expected) {
      print $2
      exit
    }
  }
')
[[ -n "${node_interface}" ]] || die "cannot resolve node interface"
cat >"${TMP_ROOT}/keepalived.conf" <<EOF
global_defs {
    router_id Q041_TEST
}

vrrp_instance Q041_TEST {
    state BACKUP
    interface ${node_interface}
    virtual_router_id 240
    priority 1
    advert_int 10
    virtual_ipaddress {
        192.0.2.254/32 dev ${node_interface}
    }
}
EOF
/usr/local/sbin/keepalived --config-test --use-file="${TMP_ROOT}/keepalived.conf" \
  >"${EVIDENCE_DIR}/keepalived-temp-config-test.txt" 2>&1

if ! /usr/bin/orchestrator --version >"${EVIDENCE_DIR}/orchestrator-self-check.txt" 2>&1; then
  /usr/bin/orchestrator version >"${EVIDENCE_DIR}/orchestrator-self-check.txt" 2>&1
fi

cp "${TMP_ROOT}/nginx-stdout.log" "${EVIDENCE_DIR}/nginx-temp-runtime.log"
cp "${TMP_ROOT}/haproxy-stdout.log" "${EVIDENCE_DIR}/haproxy-temp-runtime.log"
cp "${TMP_ROOT}/sentinel5-stdout.log" "${EVIDENCE_DIR}/sentinel5-temp-runtime.log"
cp "${TMP_ROOT}/sentinel7-stdout.log" "${EVIDENCE_DIR}/sentinel7-temp-runtime.log"

cleanup
sleep 1

if ss -lntupH | grep -Eq ":(${TEMP_PORTS}|${TARGET_PORTS})([[:space:]]|$)"; then
  die "temporary or target listener remains after cleanup"
fi
if ip -o -4 addr show | grep -Eq "${VIP_RE}"; then
  die "a project VIP is announced after temporary checks"
fi
for unit in "${UNITS[@]}"; do
  enabled_state=$(systemctl is-enabled "${unit}" 2>/dev/null || true)
  [[ "${enabled_state}" == "disabled" || "${enabled_state}" == "masked" ]] || \
    die "unit is not disabled after temporary checks: ${unit}"
  if systemctl is-active --quiet "${unit}"; then
    die "unit is active after temporary checks: ${unit}"
  fi
done

{
  printf 'captured_at=%s\n' "$(date --iso-8601=seconds)"
  printf 'run_id=%s\n' "${RUN_ID}"
  printf 'nginx_loopback_health=PASS\n'
  printf 'haproxy_loopback_health=PASS\n'
  printf 'keepalived_config_test=PASS\n'
  printf 'orchestrator_executable_self_check=PASS\n'
  printf 'redis5_sentinel_loopback_health=PASS\n'
  printf 'redis7_sentinel_loopback_health=PASS\n'
  printf 'temporary_listener_count_after_cleanup=0\n'
  printf 'target_listener_count_after_cleanup=0\n'
  printf 'vip_announcement_count=0\n'
  printf 'target_units_disabled_inactive=PASS\n'
  printf 'temporary_health_check=PASS\n'
} >"${EVIDENCE_DIR}/temp-health-result.env"

chmod 0600 "${EVIDENCE_DIR}"/*
printf 'Q041_TEMP_HEALTH_OK\n'
cat "${EVIDENCE_DIR}/temp-health-result.env"
