#!/usr/bin/env bash
#
# wg-add-client.sh — add one device to the WireGuard server you built with
# wg-setup.sh, and print a ready-to-import client config (+ a QR code for
# phones).  https://domainless.fun/vpn
#
#   sudo bash wg-add-client.sh <name>
#   e.g.  sudo bash wg-add-client.sh laptop
#
# It generates the client keypair, reserves the next free address, wires the
# peer into the running server (no restart, no dropped connections), and hands
# you the .conf. The client's private key is generated here and printed once —
# it is never stored on the server.
#
set -euo pipefail

WG_IFACE="${WG_IFACE:-wg0}"
WG_DIR="/etc/wireguard"
WG_CONF="${WG_DIR}/${WG_IFACE}.conf"
NAME="${1:-client}"

die() { echo "error: $*" >&2; exit 1; }
[ "$(id -u)" = "0" ] || die "run me as root (try: sudo bash $0 $NAME)"
[ -f "$WG_CONF" ] || die "no ${WG_CONF} — run wg-setup.sh first"
command -v wg >/dev/null || die "wireguard-tools not installed"

# ---- pull what we need from the server config -------------------------------
SERVER_PUB="$(cat "${WG_DIR}/server.pub" 2>/dev/null || true)"
[ -n "$SERVER_PUB" ] || die "missing ${WG_DIR}/server.pub"
PORT="$(awk -F'= *' '/^ListenPort/{print $2; exit}' "$WG_CONF")"
SRV_ADDR="$(awk -F'[ =/]+' '/^Address/{print $2; exit}' "$WG_CONF")"   # e.g. 10.7.0.1
SUBNET="${SRV_ADDR%.*}"                                                 # e.g. 10.7.0
WG_DNS="${WG_DNS:-1.1.1.1}"
PUBIP="${WG_ENDPOINT:-$(curl -fsS4 https://api.ipify.org 2>/dev/null || echo 'YOUR.SERVER.IP')}"

# ---- find the next free address in the subnet -------------------------------
declare -A used
used["${SRV_ADDR##*.}"]=1
while read -r oct; do used["$oct"]=1; done < <(
  grep -oE "AllowedIPs *= *${SUBNET//./\\.}\.[0-9]+" "$WG_CONF" | grep -oE '[0-9]+$'
)
NEXT=""
for i in $(seq 2 254); do
  [ -z "${used[$i]:-}" ] && { NEXT="$i"; break; }
done
[ -n "$NEXT" ] || die "no free addresses left in ${SUBNET}.0/24"
CLIENT_ADDR="${SUBNET}.${NEXT}"

# ---- generate the client keypair --------------------------------------------
CLIENT_PRIV="$(wg genkey)"
CLIENT_PUB="$(printf '%s' "$CLIENT_PRIV" | wg pubkey)"

# ---- wire the peer into the live server + persist it ------------------------
cp -a "$WG_CONF" "${WG_CONF}.bak.$(date +%s)"
cat >> "$WG_CONF" <<EOF

# ${NAME} — added $(date -u +%Y-%m-%dT%H:%MZ)
[Peer]
PublicKey = ${CLIENT_PUB}
AllowedIPs = ${CLIENT_ADDR}/32
EOF
# apply to the running interface without dropping existing peers
wg set "$WG_IFACE" peer "$CLIENT_PUB" allowed-ips "${CLIENT_ADDR}/32"

# ---- build the client .conf -------------------------------------------------
OUT="${WG_DIR}/clients/${NAME}.conf"
mkdir -p "${WG_DIR}/clients"; umask 077
cat > "$OUT" <<EOF
[Interface]
PrivateKey = ${CLIENT_PRIV}
Address = ${CLIENT_ADDR}/32
DNS = ${WG_DNS}

[Peer]
PublicKey = ${SERVER_PUB}
Endpoint = ${PUBIP}:${PORT}
# All traffic through the tunnel. For a split tunnel (only reach the VPN
# subnet), change this to: AllowedIPs = ${SUBNET}.0/24
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF

# ---- hand it over -----------------------------------------------------------
echo "────────────────────────────────────────────────────────────"
echo "  Added '${NAME}' as ${CLIENT_ADDR}. Config saved to ${OUT}"
echo "────────────────────────────────────────────────────────────"
cat "$OUT"
echo "────────────────────────────────────────────────────────────"
if command -v qrencode >/dev/null 2>&1; then
  echo "  Scan this in the WireGuard phone app (+ → Create from QR code):"
  qrencode -t ANSIUTF8 < "$OUT"
fi
echo "  Import the .conf (or QR) into the official WireGuard app and connect."
