#!/usr/bin/env bash

set -Eeuo pipefail

SCRIPT_NAME="$(basename "$0")"
SYSCTL_FILE="/etc/sysctl.d/99-tailscale-exit-node.conf"
ENABLE_IPV6=1
ADVERTISE=1
DRY_RUN=0

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

warn() {
  printf '[WARN] %s\n' "$*" >&2
}

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

usage() {
  cat <<'EOF'
Usage:
  sudo ./setup-tailscale-exit-node.sh [options]

Options:
  --no-ipv6          Do not configure IPv6 forwarding.
  --skip-advertise   Do not run tailscale advertise command.
  --dry-run          Show actions without changing the machine.
  -h, --help         Show this help.
EOF
}

run() {
  if [[ "${DRY_RUN}" -eq 1 ]]; then
    printf '[DRY-RUN] '
    printf '%q ' "$@"
    printf '\n'
    return 0
  fi

  "$@"
}

require_root() {
  if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
    die "Please run as root: sudo ${SCRIPT_NAME}"
  fi
}

require_cmd() {
  command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --no-ipv6)
        ENABLE_IPV6=0
        ;;
      --skip-advertise)
        ADVERTISE=0
        ;;
      --dry-run)
        DRY_RUN=1
        ;;
      -h|--help)
        usage
        exit 0
        ;;
      *)
        die "Unknown option: $1"
        ;;
    esac
    shift
  done
}

set_runtime_forwarding() {
  log "Enabling runtime IPv4 forwarding"
  run sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'

  if [[ "${ENABLE_IPV6}" -eq 1 ]]; then
    if [[ -e /proc/sys/net/ipv6/conf/all/forwarding ]]; then
      log "Enabling runtime IPv6 forwarding"
      run sh -c 'echo 1 > /proc/sys/net/ipv6/conf/all/forwarding'
    else
      warn "IPv6 forwarding path not present; skipping IPv6"
      ENABLE_IPV6=0
    fi
  fi
}

write_persistent_config() {
  log "Writing persistent sysctl config to ${SYSCTL_FILE}"

  if [[ "${DRY_RUN}" -eq 1 ]]; then
    printf '[DRY-RUN] install -d /etc/sysctl.d\n'
    printf '[DRY-RUN] write %s with:\n' "${SYSCTL_FILE}"
    printf 'net.ipv4.ip_forward = 1\n'
    if [[ "${ENABLE_IPV6}" -eq 1 ]]; then
      printf 'net.ipv6.conf.all.forwarding = 1\n'
    fi
    return 0
  fi

  install -d /etc/sysctl.d
  cat > "${SYSCTL_FILE}" <<EOF
net.ipv4.ip_forward = 1
EOF

  if [[ "${ENABLE_IPV6}" -eq 1 ]]; then
    cat >> "${SYSCTL_FILE}" <<EOF
net.ipv6.conf.all.forwarding = 1
EOF
  fi
}

load_persistent_config() {
  log "Loading sysctl config from ${SYSCTL_FILE}"
  run sysctl -p "${SYSCTL_FILE}"
}

verify_forwarding() {
  [[ "${DRY_RUN}" -eq 0 ]] || return 0

  local ipv4_value
  ipv4_value="$(sysctl -n net.ipv4.ip_forward)"
  [[ "${ipv4_value}" == "1" ]] || die "IPv4 forwarding verification failed: got ${ipv4_value}"
  log "Verified IPv4 forwarding = 1"

  if [[ "${ENABLE_IPV6}" -eq 1 ]]; then
    local ipv6_value
    ipv6_value="$(sysctl -n net.ipv6.conf.all.forwarding)"
    [[ "${ipv6_value}" == "1" ]] || die "IPv6 forwarding verification failed: got ${ipv6_value}"
    log "Verified IPv6 forwarding = 1"
  fi
}

advertise_exit_node() {
  [[ "${ADVERTISE}" -eq 1 ]] || {
    log "Skipping Tailscale advertise step by request"
    return 0
  }

  if tailscale set --help 2>&1 | grep -q -- '--advertise-exit-node'; then
    log "Advertising exit node with tailscale set --advertise-exit-node"
    run tailscale set --advertise-exit-node
  else
    log "Advertising exit node with tailscale up --advertise-exit-node"
    run tailscale up --advertise-exit-node
  fi
}

show_result() {
  cat <<'EOF'

Done.

Verify again with:
  sysctl net.ipv4.ip_forward
  sysctl net.ipv6.conf.all.forwarding
  tailscale status

Then:
  1. Approve "Use as exit node" in the Tailscale admin console if still pending
  2. On the client, select this machine as the Exit Node
  3. Test from the client:
     curl https://ifconfig.me
EOF
}

main() {
  parse_args "$@"
  require_root
  require_cmd tailscale
  require_cmd sysctl

  set_runtime_forwarding
  write_persistent_config
  load_persistent_config
  verify_forwarding
  advertise_exit_node
  show_result
}

main "$@"
