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

RUNTIME_ENV_FILE="${WELLAU_SSLVPN_RUNTIME_ENV_FILE:-$HOME/.config/wellau-sslvpn/runtime.env}"
if [[ -f "$RUNTIME_ENV_FILE" ]]; then
  # shellcheck disable=SC1090
  source "$RUNTIME_ENV_FILE"
fi

REPO_DIR="${SSLVPN_REPO_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
ENV_FILE="${WELLAU_SSLVPN_ENV_FILE:-$REPO_DIR/.env}"
PID_FILE="${WELLAU_VPN_PID_FILE:-/tmp/wellau-vpn.pid}"
OPENCONNECT_BIN="${OPENCONNECT_BIN:-/usr/sbin/openconnect}"
OPENCONNECT_DISABLE_DTLS="${OPENCONNECT_DISABLE_DTLS:-0}"
VPN_RESOLVE_IP="${WELLAU_VPN_RESOLVE_IP:-43.198.49.0}"
POST_CONNECT_SLEEP_SECONDS="${POST_CONNECT_SLEEP_SECONDS:-8}"
STOP_OLD_BEFORE_AUTH="${WELLAU_VPN_STOP_OLD_BEFORE_AUTH:-0}"
SUDO_ARGS=(sudo)
if [[ "${WELLAU_VPN_NONINTERACTIVE_SUDO:-0}" == "1" ]]; then
  SUDO_ARGS=(sudo -n)
fi

if [[ -f "$ENV_FILE" ]]; then
  set -a
  # shellcheck disable=SC1090
  source "$ENV_FILE"
  set +a
fi

VPN_GATEWAY="${WELLAU_VPN_GATEWAY:-vpn.wellau.com}"
VPN_USERNAME="${WELLAU_VPN_USERNAME:-}"
VPN_PASSWORD="${WELLAU_VPN_PASSWORD:-}"
VPN_TOTP_SECRET="${WELLAU_VPN_TOTP_SECRET:-}"

log() {
  printf '%s\n' "$*"
}

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

require_secret() {
  local name="$1"
  local value="$2"
  [[ -n "$value" ]] || fail "$name is missing in $ENV_FILE"
}

compute_totp() {
  python3 - "$1" <<'PY'
import base64
import hashlib
import hmac
import struct
import sys
import time

secret = sys.argv[1].strip().replace(" ", "").upper()
padding = "=" * ((8 - (len(secret) % 8)) % 8)
key = base64.b32decode(secret + padding)
counter = int(time.time()) // 30
msg = struct.pack(">Q", counter)
digest = hmac.new(key, msg, hashlib.sha1).digest()
offset = digest[-1] & 0x0F
code = (struct.unpack(">I", digest[offset:offset + 4])[0] & 0x7FFFFFFF) % 1000000
print(f"{code:06d}")
PY
}

require_secret WELLAU_VPN_USERNAME "$VPN_USERNAME"
require_secret WELLAU_VPN_PASSWORD "$VPN_PASSWORD"
require_secret WELLAU_VPN_TOTP_SECRET "$VPN_TOTP_SECRET"

if [[ ! -x "$OPENCONNECT_BIN" ]]; then
  fail "openconnect binary is missing or not executable: $OPENCONNECT_BIN"
fi

if [[ "${WELLAU_VPN_NONINTERACTIVE_SUDO:-0}" == "1" ]]; then
  if ! "${SUDO_ARGS[@]}" "$OPENCONNECT_BIN" --version >/dev/null 2>&1; then
    fail "passwordless sudo is required for $OPENCONNECT_BIN; run scripts/install-user-health-timer.sh"
  fi
elif ! "${SUDO_ARGS[@]}" -v; then
  fail "sudo authentication is required to start openconnect"
fi

stop_existing_tunnel() {
  if [[ -s "$PID_FILE" ]]; then
    old_pid="$(cat "$PID_FILE" 2>/dev/null || true)"
    if [[ -n "$old_pid" ]] && ps -p "$old_pid" >/dev/null 2>&1; then
      log "Stopping existing openconnect pid=$old_pid"
      "${SUDO_ARGS[@]}" kill "$old_pid" || true
      sleep 2
    fi
  fi
  "${SUDO_ARGS[@]}" rm -f "$PID_FILE"
}

if [[ "$STOP_OLD_BEFORE_AUTH" == "1" ]]; then
  log "Stopping existing tunnel before authentication because forced reconnect was requested"
  stop_existing_tunnel
fi

totp_code="$(compute_totp "$VPN_TOTP_SECRET")"
dtls_args=()
if [[ "$OPENCONNECT_DISABLE_DTLS" == "1" ]]; then
  dtls_args+=(--no-dtls)
fi

log "Authenticating to $VPN_GATEWAY as $VPN_USERNAME using $ENV_FILE"
auth_stdout="$(mktemp)"
auth_stderr="$(mktemp)"
cleanup_auth_files() {
  rm -f "$auth_stdout" "$auth_stderr"
}
trap cleanup_auth_files EXIT

if ! timeout 60s "$OPENCONNECT_BIN" \
  --authenticate \
  --protocol=anyconnect \
  "${dtls_args[@]}" \
  --non-inter \
  --user "$VPN_USERNAME" \
  --form-entry main:password="$totp_code" \
  --form-entry main:secondary_password="$VPN_PASSWORD" \
  "$VPN_GATEWAY" >"$auth_stdout" 2>"$auth_stderr"; then
  sed -E 's/(password|secondary_password|COOKIE|cookie|PASS|TOKEN|token)=([^ ]+)/\1=<redacted>/Ig' "$auth_stdout" >&2 || true
  sed -E 's/(password|secondary_password|COOKIE|cookie|PASS|TOKEN|token)=([^ ]+)/\1=<redacted>/Ig' "$auth_stderr" >&2 || true
  fail "openconnect authentication failed"
fi

COOKIE=""
CONNECT_URL=""
FINGERPRINT=""
RESOLVE=""
auth_vars="$(grep -E '^(COOKIE|CONNECT_URL|FINGERPRINT|RESOLVE)=' "$auth_stdout" || true)"
if [[ -z "$auth_vars" ]]; then
  sed -E 's/(password|secondary_password|COOKIE|cookie|PASS|TOKEN|token)=([^ ]+)/\1=<redacted>/Ig' "$auth_stdout" >&2 || true
  sed -E 's/(password|secondary_password|COOKIE|cookie|PASS|TOKEN|token)=([^ ]+)/\1=<redacted>/Ig' "$auth_stderr" >&2 || true
  fail "openconnect did not return authentication metadata"
fi
eval "$auth_vars"

[[ -n "${COOKIE:-}" ]] || fail "openconnect did not return COOKIE"
[[ -n "${CONNECT_URL:-}" ]] || fail "openconnect did not return CONNECT_URL"
[[ -n "${FINGERPRINT:-}" ]] || fail "openconnect did not return FINGERPRINT"

if [[ "$STOP_OLD_BEFORE_AUTH" != "1" ]]; then
  stop_existing_tunnel
fi

start_args=(
  --protocol=anyconnect
  --cookie-on-stdin
  "$CONNECT_URL"
  --servercert "$FINGERPRINT"
  --background
  --pid-file "$PID_FILE"
)
start_args+=("${dtls_args[@]}")
if [[ -n "$VPN_RESOLVE_IP" ]]; then
  start_args+=(--resolve "$VPN_GATEWAY:$VPN_RESOLVE_IP")
elif [[ -n "${RESOLVE:-}" ]]; then
  start_args+=(--resolve "$RESOLVE")
fi

log "Starting openconnect background process"
printf '%s' "$COOKIE" | "${SUDO_ARGS[@]}" "$OPENCONNECT_BIN" "${start_args[@]}"
sleep "$POST_CONNECT_SLEEP_SECONDS"

if [[ -s "$PID_FILE" ]]; then
  log "Connected: pid=$(cat "$PID_FILE")"
else
  fail "pid file was not created: $PID_FILE"
fi

if [[ -x "$HOME/.local/bin/wellau-vpn-status" ]]; then
  exec "$HOME/.local/bin/wellau-vpn-status" --fresh
fi
exec bash "$REPO_DIR/bin/wellau-vpn-status" --fresh
