#!/bin/sh
set -eu

# Hikaproj Tunnel - Agent installer
# Preferred mode: the Server CLI generates a personalized copy containing
# HIKAPROJ_GATEWAY and HIKAPROJ_BOOTSTRAP_TOKEN. Then the Agent operator only
# needs to execute this script once; all later configuration is pushed remotely.

HIKAPROJ_GATEWAY="${HIKAPROJ_GATEWAY:-__BAKED_GATEWAY__}"
HIKAPROJ_BOOTSTRAP_TOKEN="${HIKAPROJ_BOOTSTRAP_TOKEN:-__BAKED_TOKEN__}"
HIKAPROJ_VERSION="${HIKAPROJ_VERSION:-latest}"
RELEASE_BASE_URL="${HIKAPROJ_RELEASE_BASE_URL:-https://github.com/Hikaproj/hikaproj-tunnel/releases/download}"
INSTALL_DIR="${HIKAPROJ_INSTALL_DIR:-/usr/local/bin}"
CONF_DIR="/etc/hikaproj"
STATE_DIR="/var/lib/hikaproj-agent"
SERVICE="hikaproj-agent.service"
BINARY="hikaproj-agent"
TMP=""

say(){ printf '%s\n' "[hikaproj-agent] $*"; }
fail(){ printf '%s\n' "[hikaproj-agent] ERROR: $*" >&2; exit 1; }
cleanup(){ [ -n "${TMP}" ] && rm -rf "${TMP}" 2>/dev/null || true; }
trap cleanup EXIT INT TERM

[ "$(id -u)" -eq 0 ] || fail "root権限が必要です。sudo sh install-agent.sh を実行してください。"
[ "$HIKAPROJ_GATEWAY" != "__BAKED_GATEWAY__" ] || fail "Gateway情報が未設定です。Server側で hpjtunnel agent create --install-command を生成してください。"
[ "$HIKAPROJ_BOOTSTRAP_TOKEN" != "__BAKED_TOKEN__" ] || fail "Bootstrap tokenが未設定です。Server側で生成したinstall-agent.shを使用してください。"

ARCH="$(uname -m)"
case "$ARCH" in
  x86_64|amd64) ARCH="amd64" ;;
  aarch64|arm64) ARCH="arm64" ;;
  armv7l|armv7) ARCH="armv7" ;;
  *) fail "未対応architecture: $ARCH" ;;
esac

OS_ID="unknown"
OS_VER="unknown"
[ -r /etc/os-release ] && . /etc/os-release && OS_ID="${ID:-unknown}" && OS_VER="${VERSION_ID:-unknown}"
case "$OS_ID" in
  debian|ubuntu|linuxmint|raspbian) PM="apt-get" ;;
  alpine) PM="apk" ;;
  fedora|rhel|rocky|almalinux) PM="dnf" ;;
  centos) PM="yum" ;;
  arch|manjaro) PM="pacman" ;;
  opensuse*|sles) PM="zypper" ;;
  *) PM="" ;;
esac

command_exists(){ command -v "$1" >/dev/null 2>&1; }
install_bootstrap_tools(){
  if command_exists wget || command_exists curl || command_exists python3 || command_exists python || command_exists busybox; then return 0; fi
  [ -n "$PM" ] || fail "取得手段がなくpackage managerも不明です。オフラインinstallerを使用してください。"
  case "$PM" in
    apt-get) export DEBIAN_FRONTEND=noninteractive; apt-get update; apt-get install -y ca-certificates wget tar gzip xz-utils ;;
    apk) apk add --no-cache ca-certificates wget tar gzip xz ;;
    dnf) dnf install -y ca-certificates wget tar gzip xz ;;
    yum) yum install -y ca-certificates wget tar gzip xz ;;
    pacman) pacman -Sy --noconfirm ca-certificates wget tar gzip xz ;;
    zypper) zypper --non-interactive install ca-certificates wget tar gzip xz ;;
  esac
}
fetch(){
  url="$1"; out="$2"
  if command_exists wget; then wget -q --https-only -O "$out" "$url" && return 0; fi
  if command_exists curl; then curl -fL --proto '=https' --tlsv1.2 -o "$out" "$url" && return 0; fi
  if command_exists python3; then python3 - "$url" "$out" <<'PY'
import sys, urllib.request
u, o = sys.argv[1], sys.argv[2]
req = urllib.request.Request(u, headers={"User-Agent": "hikaproj-agent-installer"})
with urllib.request.urlopen(req, timeout=30) as r, open(o, "wb") as f: f.write(r.read())
PY
  return 0; fi
  if command_exists python; then python - "$url" "$out" <<'PY'
import sys, urllib.request
u, o = sys.argv[1], sys.argv[2]
req = urllib.request.Request(u, headers={"User-Agent": "hikaproj-agent-installer"})
with urllib.request.urlopen(req, timeout=30) as r, open(o, "wb") as f: f.write(r.read())
PY
  return 0; fi
  if command_exists busybox; then busybox wget -q -O "$out" "$url" && return 0; fi
  return 1
}
verify_sha256(){
  file="$1"; sums="$2"; name="$3"
  if command_exists sha256sum; then sha256sum -c "$sums" --ignore-missing >/dev/null 2>&1; return $?; fi
  if command_exists shasum; then expected="$(awk -v n="$name" '$2==n{print $1; exit}' "$sums")"; actual="$(shasum -a 256 "$file" | awk '{print $1}')"; [ -n "$expected" ] && [ "$expected" = "$actual" ]; return $?; fi
  if command_exists openssl; then expected="$(awk -v n="$name" '$2==n{print $1; exit}' "$sums")"; actual="$(openssl dgst -sha256 "$file" | awk '{print $NF}')"; [ -n "$expected" ] && [ "$expected" = "$actual" ]; return $?; fi
  return 1
}

install_bootstrap_tools
mkdir -p "$CONF_DIR" "$STATE_DIR" "$INSTALL_DIR"
chmod 0750 "$CONF_DIR" "$STATE_DIR"
TMP="$(mktemp -d /tmp/hikaproj-agent.XXXXXX)"
case "$HIKAPROJ_VERSION" in latest) RELEASE_PATH="latest/download" ;; *) RELEASE_PATH="$HIKAPROJ_VERSION" ;; esac
ARCHIVE="hikaproj-agent-${ARCH}-linux.tar.gz"
URL="$RELEASE_BASE_URL/$RELEASE_PATH/$ARCHIVE"
SUMS="$TMP/SHA256SUMS"

say "OS=$OS_ID $OS_VER arch=$ARCH"
say "Connecting to gateway: $HIKAPROJ_GATEWAY"
if [ -n "${HIKAPROJ_LOCAL_ARCHIVE:-}" ]; then cp "$HIKAPROJ_LOCAL_ARCHIVE" "$TMP/$ARCHIVE"; else fetch "$URL" "$TMP/$ARCHIVE" || fail "Agent release取得に失敗しました。オフラインinstallerを使用してください。"; fi
fetch "$URL.sha256" "$SUMS" || fail "checksum取得失敗"
verify_sha256 "$TMP/$ARCHIVE" "$SUMS" "$ARCHIVE" || fail "SHA-256検証失敗"
tar -xzf "$TMP/$ARCHIVE" -C "$TMP"
[ -x "$TMP/$BINARY" ] || fail "$BINARY がrelease内にありません。"
install -m 0755 "$TMP/$BINARY" "$INSTALL_DIR/$BINARY"

# Only the agent needs to know the bootstrap values once. The bootstrap token
# is exchanged for a permanent rotated credential and then deleted by the
# running agent; the installer does not persist it in shell history or logs.
cat > "$CONF_DIR/agent.yaml" <<CFG
agent:
  gateway: $HIKAPROJ_GATEWAY
  bootstrap_token: $HIKAPROJ_BOOTSTRAP_TOKEN
storage:
  path: $STATE_DIR/agent.db
runtime:
  auto_update: true
CFG
chmod 0600 "$CONF_DIR/agent.yaml"

if ! getent group hikaproj >/dev/null 2>&1; then groupadd --system hikaproj; fi
if ! id hikaproj >/dev/null 2>&1; then useradd --system --gid hikaproj --home-dir "$STATE_DIR" --shell /usr/sbin/nologin hikaproj; fi
chown root:hikaproj "$CONF_DIR/agent.yaml"; chmod 0640 "$CONF_DIR/agent.yaml"
chown -R hikaproj:hikaproj "$STATE_DIR"

cat > /etc/systemd/system/$SERVICE <<UNIT
[Unit]
Description=Hikaproj Tunnel Agent
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=hikaproj
Group=hikaproj
ExecStart=$INSTALL_DIR/$BINARY run --config $CONF_DIR/agent.yaml
Restart=always
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=$STATE_DIR

[Install]
WantedBy=multi-user.target
UNIT

systemctl daemon-reload
systemctl enable --now "$SERVICE"
sleep 1
systemctl is-active --quiet "$SERVICE" || { journalctl -u "$SERVICE" -n 50 --no-pager >&2 || true; fail "Agent service起動失敗"; }

say "Agent installation complete."
say "このPCでの追加設定は不要です。"
say "Route/target/limit/credential/updateはGateway側CLIから管理します。"
