#!/usr/bin/env bash
# protonvpn-cli 0.10.0 — extra-repo proton-vpn-cli + session autostart files.
# Does not run the vendor protonvpn CLI (signin/getpass/keyring must be the desktop user).
# Class: pipeable
# Brace group: bash slurps the whole file from a pipe before running (curl | sudo bash).
{
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive

VERSION="0.10.0"
RELEASE_DEB_URL="https://repo.protonvpn.com/debian/dists/stable/main/binary-all/protonvpn-stable-release_1.0.8_all.deb"
RELEASE_DEB_SHA256="0b14e71586b22e498eb20926c48c7b434b751149b1f2af9902ef1cfe6b03e180"
RELEASE_DEB_NAME="protonvpn-stable-release_1.0.8_all.deb"
REPO_PKG="protonvpn-stable-release"
CLI_PKG="proton-vpn-cli"
GUI_PKG="proton-vpn-gnome-desktop"
OLD_CLI_PKG="protonvpn-cli"
AUTOSTART_UNIT_NAME="protonvpn-connect.service"
SYSTEMCTL_SEC=8

DRY_RUN=0
AUTOSTART_ENABLED=0
DEB_TMPDIR=""

if [[ -n "${BASH_SOURCE[0]:-}" && -f "${BASH_SOURCE[0]}" ]]; then
  SCRIPT_PATH=$(readlink -f "${BASH_SOURCE[0]}")
else
  SCRIPT_PATH=""
fi

err() { printf '%s\n' "$*" >&2; }
log() { printf '%s\n' "$*"; }

die_usage() {
  err "error: $*"
  err "Try --help for invoke."
  exit 1
}

die_run() {
  err "error: $*"
  exit 2
}

cleanup_deb_tmp() {
  if [[ -n "${DEB_TMPDIR}" && -d "${DEB_TMPDIR}" ]]; then
    rm -rf "${DEB_TMPDIR}"
  fi
}
trap cleanup_deb_tmp EXIT

usage() {
  cat <<EOF
protonvpn-cli ${VERSION} — Proton extra repo + proton-vpn-cli + session autostart files.

Operator invoke:
  curl -fsSL https://debian.reporet.com/protonvpncli.sh | sudo bash
  sudo bash scripts/protonvpn/protonvpncli.sh

This script does not run protonvpn (no sign-in, no config set, no connect).
After it exits, run protonvpn signin as your desktop user (no sudo).

Developer-only:
  bash scripts/protonvpn/protonvpncli.sh --help
  bash scripts/protonvpn/protonvpncli.sh --dry-run

Do not use curl | bash without sudo (cannot re-exec a pipe).
Do not write curl …/protonvpncli.sh --dry-run | sudo bash (curl consumes the flag; use bash -s --).

Options:
  --dry-run   Print commands; no dpkg, apt, writes, or systemctl
  --help      Print this text and exit 0

Exit codes: 0 success; 1 usage/preflight; 2 runtime.
EOF
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --help)
        usage
        exit 0
        ;;
      --dry-run) DRY_RUN=1 ;;
      --)
        shift
        break
        ;;
      -*)
        die_usage "unknown option: $1"
        ;;
      *)
        die_usage "unexpected argument: $1"
        ;;
    esac
    shift
  done
  if [[ $# -gt 0 ]]; then
    die_usage "unexpected argument: $1"
  fi
}

login_user() {
  if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != root ]]; then
    printf '%s\n' "$SUDO_USER"
    return 0
  fi
  local u
  u=$(logname 2>/dev/null || true)
  if [[ -n "$u" && "$u" != root ]]; then
    printf '%s\n' "$u"
    return 0
  fi
  if [[ -n "${USER:-}" && "${USER}" != root ]]; then
    printf '%s\n' "$USER"
    return 0
  fi
  printf 'root\n'
}

login_home() {
  local u home
  u=$(login_user)
  home=$(getent passwd "$u" | awk -F: '{print $6}')
  if [[ -n "$home" ]]; then
    printf '%s\n' "$home"
    return 0
  fi
  printf '/home/%s\n' "$u"
}

# GNU env treats -- after NAME=VALUE as the utility. sudo's -- stays.
# Never used to invoke protonvpn (vendor CLI is the desktop user, no sudo).
run_as_login() {
  local user uid runtime
  user=$(login_user)
  if [[ "$user" == root ]] || [[ "$(id -un)" == "$user" ]]; then
    "$@"
    return
  fi
  uid=$(id -u "$user")
  runtime="/run/user/${uid}"
  if command -v env >/dev/null 2>&1; then
    sudo -u "$user" -H -- env \
      "XDG_RUNTIME_DIR=${runtime}" \
      "DBUS_SESSION_BUS_ADDRESS=unix:path=${runtime}/bus" \
      "$@"
  else
    sudo -u "$user" -H \
      "XDG_RUNTIME_DIR=${runtime}" \
      "DBUS_SESSION_BUS_ADDRESS=unix:path=${runtime}/bus" \
      "$@"
  fi
}

timeout_bin() {
  if [[ -x /usr/bin/timeout ]]; then
    printf '%s\n' /usr/bin/timeout
  elif command -v timeout >/dev/null 2>&1; then
    command -v timeout
  fi
}

# Never pass a bare double-dash to GNU timeout (coreutils 9.7 treats it as the command).
run_as_login_timeout() {
  local sec=$1
  local tb
  shift
  tb=$(timeout_bin)
  if [[ -n "$tb" ]]; then
    run_as_login "$tb" -k 1 "$sec" "$@" </dev/null
  else
    run_as_login "$@" </dev/null
  fi
}

pkg_installed() {
  local st
  st=$(dpkg-query -W -f='${Status}' "$1" 2>/dev/null) || return 1
  [[ "$st" == "install ok installed" ]]
}

have_network() {
  if command -v ip >/dev/null 2>&1 && ip route show default 2>/dev/null | grep -q .; then
    return 0
  fi
  local d iface carrier
  for d in /sys/class/net/*; do
    [[ -e "$d" ]] || continue
    iface=$(basename "$d")
    [[ "$iface" != lo ]] || continue
    carrier=$(cat "$d/carrier" 2>/dev/null || printf '0')
    if [[ "$carrier" == 1 ]]; then
      return 0
    fi
  done
  return 1
}

# Fail immediately when another apt/dpkg holds the lock (retries otherwise wait forever).
apt_locks_free() {
  local f
  command -v flock >/dev/null 2>&1 || return 0
  for f in /var/lib/dpkg/lock-frontend /var/lib/apt/lists/lock; do
    [[ -e "$f" ]] || continue
    if ! flock --nonblock "$f" -c true 2>/dev/null; then
      return 1
    fi
  done
  return 0
}

wrapper_path() {
  printf '%s/.local/lib/debian-os-setup/protonvpn/protonvpn-connect-startup.sh\n' "$(login_home)"
}

linux_wrapper_path() {
  printf '%s/.local/lib/debian-linux/protonvpn/protonvpn-connect-startup.sh\n' "$(login_home)"
}

unit_path() {
  printf '%s/.config/systemd/user/%s\n' "$(login_home)" "$AUTOSTART_UNIT_NAME"
}

preflight() {
  if [[ ! -f /etc/os-release ]]; then
    die_usage "cannot read /etc/os-release; Debian is required"
  fi
  # ID=debian only; Ubuntu and derivatives set a different ID.
  # shellcheck source=/dev/null
  . /etc/os-release
  if [[ "${ID:-}" != debian ]]; then
    die_usage "this script requires Debian (ID=debian); refusing ID=${ID:-unknown} (Ubuntu is not supported)"
  fi
  if [[ "$(login_user)" == root ]]; then
    die_usage "run via sudo from the desktop user so SUDO_USER is set (do not run as root login)"
  fi
  if pkg_installed "$GUI_PKG"; then
    die_usage "${GUI_PKG} is installed. The official CLI and GUI must not run together. Purge it: sudo apt-get purge ${GUI_PKG}"
  fi
  if pkg_installed "$OLD_CLI_PKG"; then
    die_usage "community package ${OLD_CLI_PKG} is installed. Purge it: sudo apt-get purge ${OLD_CLI_PKG}"
  fi
  if ! have_network; then
    if ((DRY_RUN)); then
      err "warn: no default route or carrier; dry-run continues"
    else
      die_usage "no network (no default route). Run after GNOME Settings owns wifi or ethernet"
    fi
  fi
  if ((EUID == 0)) && ! apt_locks_free; then
    if ((DRY_RUN)); then
      err "warn: dpkg/apt lock is held; a live run would exit 2"
    else
      die_run "dpkg/apt is locked (another apt/dpkg is running, or a prior attempt left a lock). Wait for it to finish, then re-run"
    fi
  fi
  if command -v systemctl >/dev/null 2>&1; then
    local en
    en=$(systemctl is-enabled NetworkManager 2>/dev/null || true)
    if [[ "$en" != enabled ]]; then
      err "warn: NetworkManager is not enabled. Sign-in and connect need NM after GNOME login. This script does not start NM."
    fi
  fi
}

maybe_sudo_reexec() {
  ((EUID == 0)) && return 0
  command -v sudo >/dev/null 2>&1 || die_usage "root or sudo is required"
  if [[ -n "$SCRIPT_PATH" && -f "$SCRIPT_PATH" ]]; then
    exec sudo bash "$SCRIPT_PATH" "$@"
  fi
  die_usage "piped invoke requires root. Use: curl -fsSL https://debian.reporet.com/protonvpncli.sh | sudo bash"
}

install_cli() {
  log "Fence: install (${REPO_PKG} + ${CLI_PKG})"
  if pkg_installed "$REPO_PKG" && pkg_installed "$CLI_PKG"; then
    log "Already installed: ${REPO_PKG} ${CLI_PKG}"
    return 0
  fi
  if ((DRY_RUN)); then
    if ! pkg_installed "$REPO_PKG"; then
      printf 'dry-run: curl -fsSL %s -o %s\n' "$RELEASE_DEB_URL" "$RELEASE_DEB_NAME"
      printf 'dry-run: sha256sum --check  # expect %s\n' "$RELEASE_DEB_SHA256"
      printf 'dry-run: dpkg -i %s\n' "$RELEASE_DEB_NAME"
    fi
    printf 'dry-run: apt-get update\n'
    printf 'dry-run: apt-get install -y %s\n' "$CLI_PKG"
    return 0
  fi
  command -v curl >/dev/null 2>&1 || die_usage "curl is required"
  command -v sha256sum >/dev/null 2>&1 || die_usage "sha256sum is required"
  if ! pkg_installed "$REPO_PKG"; then
    DEB_TMPDIR=$(mktemp -d)
    local deb_path="${DEB_TMPDIR}/${RELEASE_DEB_NAME}"
    curl -fsSL "$RELEASE_DEB_URL" -o "$deb_path" || die_run "failed to download ${RELEASE_DEB_NAME}"
    if ! printf '%s  %s\n' "$RELEASE_DEB_SHA256" "$deb_path" | sha256sum --check -; then
      die_run "SHA-256 check failed for ${RELEASE_DEB_NAME}"
    fi
    dpkg -i "$deb_path" </dev/null || die_run "dpkg -i ${RELEASE_DEB_NAME} failed"
  fi
  apt-get update </dev/null || die_run "apt-get update failed"
  apt-get install -y "$CLI_PKG" </dev/null || die_run "apt-get install ${CLI_PKG} failed"
  pkg_installed "$CLI_PKG" || die_run "${CLI_PKG} missing after install"
}

write_autostart_wrapper() {
  cat <<'EOF'
#!/usr/bin/env bash
# Connect ProtonVPN at session start (debian-os-setup / scripts/protonvpn).
# Requires: official protonvpn CLI and a prior interactive login/config as this user.

set -euo pipefail

readonly SCRIPT_NAME="protonvpn-connect-startup.sh"
readonly PROTONVPN="/usr/bin/protonvpn"
readonly EXIT_OK=0
readonly EXIT_FAIL=1
readonly EXIT_NOT_FOUND=127
readonly STATUS_SEC=15
readonly CONNECT_SEC=45

log() {
  printf '%s %s: %s\n' "$(date -Iseconds 2>/dev/null || date)" "${SCRIPT_NAME}" "$*" >&2
}

die() {
  local code="$1"
  shift
  log "ERROR: $*"
  exit "${code}"
}

run_timeout() {
  local sec=$1
  shift
  if [[ -x /usr/bin/timeout ]]; then
    /usr/bin/timeout -k 1 "$sec" "$@"
  else
    "$@"
  fi
}

already_connected() {
  local status_out
  if ! status_out="$(run_timeout "${STATUS_SEC}" "${PROTONVPN}" status 2>/dev/null)"; then
    return 1
  fi
  if printf '%s\n' "${status_out}" | grep -Eiq \
    'status:[[:space:]]*connected|connection[[:space:]]+status:[[:space:]]*connected|[[:space:]]connected[[:space:]]*$'; then
    return 0
  fi
  return 1
}

main() {
  local rc
  if [[ ! -x "${PROTONVPN}" ]]; then
    die "${EXIT_NOT_FOUND}" "'${PROTONVPN}' not found. Install proton-vpn-cli."
  fi
  if already_connected; then
    log "Already connected; skipping protonvpn connect."
    exit "${EXIT_OK}"
  fi
  log "Running: protonvpn connect"
  if run_timeout "${CONNECT_SEC}" "${PROTONVPN}" connect; then
    log "protonvpn connect completed successfully."
    exit "${EXIT_OK}"
  else
    rc=$?
    die "${EXIT_FAIL}" "protonvpn connect failed (exit ${rc})."
  fi
}

main "$@"
EOF
}

write_autostart_unit() {
  cat <<'EOF'
[Unit]
Description=Connect ProtonVPN at session start (debian-os-setup)
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
TimeoutStartSec=60
ExecStart=%h/.local/lib/debian-os-setup/protonvpn/protonvpn-connect-startup.sh
RemainAfterExit=yes

[Install]
WantedBy=default.target
EOF
}

install_autostart() {
  local wrapper unit linux_wrapper wrapper_dir unit_dir
  wrapper=$(wrapper_path)
  unit=$(unit_path)
  linux_wrapper=$(linux_wrapper_path)
  wrapper_dir=$(dirname "$wrapper")
  unit_dir=$(dirname "$unit")
  AUTOSTART_ENABLED=0

  log "Fence: autostart (user unit ${AUTOSTART_UNIT_NAME})"
  if ((DRY_RUN)); then
    printf 'dry-run: write %s (0755)\n' "$wrapper"
    printf 'dry-run: write %s (0644, TimeoutStartSec=60)\n' "$unit"
    if [[ -e "$linux_wrapper" ]] || grep -Fq debian-linux "$unit" 2>/dev/null; then
      printf 'dry-run: systemctl --user disable --now %s  # migrate debian-linux unit\n' "$AUTOSTART_UNIT_NAME"
      printf 'dry-run: rm -f %s\n' "$linux_wrapper"
    fi
    printf 'dry-run: systemctl --user daemon-reload\n'
    printf 'dry-run: systemctl --user enable %s\n' "$AUTOSTART_UNIT_NAME"
    printf 'dry-run: not starting the unit (protonvpn connect runs at next GNOME login)\n'
    return 0
  fi

  if [[ -e "$unit" ]] && grep -Fq debian-linux "$unit"; then
    log "Migrating debian-linux ${AUTOSTART_UNIT_NAME} to debian-os-setup"
    run_as_login_timeout "$SYSTEMCTL_SEC" systemctl --user disable --now "$AUTOSTART_UNIT_NAME" || \
      err "warn: systemctl --user disable --now ${AUTOSTART_UNIT_NAME} failed or timed out (continuing)"
  fi

  run_as_login install -d -m 0755 "$wrapper_dir" "$unit_dir" || \
    die_run "install -d autostart directories failed"
  write_autostart_wrapper | run_as_login tee "$wrapper" >/dev/null || \
    die_run "write wrapper failed"
  run_as_login chmod 0755 "$wrapper" || die_run "chmod 0755 wrapper failed"
  write_autostart_unit | run_as_login tee "$unit" >/dev/null || \
    die_run "write unit failed"
  run_as_login chmod 0644 "$unit" || die_run "chmod 0644 unit failed"

  if [[ -e "$linux_wrapper" ]]; then
    run_as_login rm -f "$linux_wrapper" || err "warn: could not remove debian-linux wrapper"
    run_as_login rmdir "$(dirname "$linux_wrapper")" 2>/dev/null || true
  fi

  if ! run_as_login_timeout "$SYSTEMCTL_SEC" systemctl --user daemon-reload; then
    err "warn: systemctl --user daemon-reload failed or timed out. Enable the unit after GNOME login (printed below)."
    return 0
  fi
  if run_as_login_timeout "$SYSTEMCTL_SEC" systemctl --user enable "$AUTOSTART_UNIT_NAME"; then
    AUTOSTART_ENABLED=1
  else
    err "warn: systemctl --user enable ${AUTOSTART_UNIT_NAME} failed or timed out. Enable it after GNOME login (printed below)."
  fi
}

print_next_steps() {
  log ""
  log "Done. This script did not run protonvpn (signin uses getpass + gnome-keyring in your session)."
  log "Next, as your desktop user, no sudo, in this GNOME terminal:"
  log ""
  log "  protonvpn signin YOUR_USERNAME_OR_EMAIL"
  log "  protonvpn config set netshield malware-ads-trackers"
  log "  protonvpn disconnect"
  log "  protonvpn config set kill-switch standard"
  log "  protonvpn config set ipv6 off"
  log "  protonvpn config set anonymous-crash-reports off"
  log "  protonvpn connect"
  log ""
  if ((DRY_RUN)); then
    log "Autostart (dry-run): would enable ${AUTOSTART_UNIT_NAME} for the next GNOME login."
  elif ((AUTOSTART_ENABLED)); then
    log "Autostart: ${AUTOSTART_UNIT_NAME} is enabled. Next GNOME login runs protonvpn connect."
  else
    log "  systemctl --user daemon-reload"
    log "  systemctl --user enable ${AUTOSTART_UNIT_NAME}"
    log "Then the next GNOME login runs protonvpn connect."
  fi
}

run_main() {
  preflight
  install_cli
  install_autostart
  print_next_steps
}

parse_args "$@"

if ((DRY_RUN == 0)) && [[ ! -t 0 ]] && ((EUID != 0)); then
  err "error: stdin is not a TTY and you are not root."
  err "Pipe as root: curl -fsSL https://debian.reporet.com/protonvpncli.sh | sudo bash"
  err "Or save a file, then: sudo bash /tmp/protonvpncli.sh"
  exit 1
fi

if ((DRY_RUN == 0)); then
  maybe_sudo_reexec "$@"
fi

run_main
exit 0
}
