#!/usr/bin/env bash
#
# tunnel-install.sh — cài tunnel chạy ngầm 24/7 tới entry.io.vn (tự kết nối lại, tự bật khi boot).
# Hỗ trợ:  Linux (systemd hoặc OpenRC)  +  macOS (launchd).  Tự nhận diện OS.
# Windows: dùng tunnel-install.ps1
#
# KHÔNG cần autossh — dùng ssh thuần + service manager tự khởi động lại (systemd Restart /
# launchd KeepAlive / OpenRC supervise-daemon). ssh tự thoát khi mất kết nối (ServerAlive).
#
# Dùng:
#   Linux:  sudo ./tunnel-install.sh <subdomain> <port> [local_host]
#   macOS:       ./tunnel-install.sh <subdomain> <port> [local_host]
# VD:
#   sudo ./tunnel-install.sh myapp 3000      ->  https://myapp.entry.io.vn  (forward về localhost:3000)
#
set -euo pipefail

SISH_HOST=entry.io.vn
SISH_PORT=2222
API_BASE="${ENTRY_API_BASE:-https://app.entry.io.vn}"   # để tự đăng ký metadata dịch vụ (cần ENTRY_TOKEN)
SSH_OPTS="-o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=accept-new"

# ============================== GỠ BỎ (uninstall) ==============================
# Dùng:  sudo ./tunnel-install.sh --uninstall     (Linux)  ·  ./tunnel-install.sh --uninstall  (macOS)
if [ "${1:-}" = "--uninstall" ] || [ "${1:-}" = "uninstall" ] || [ "${1:-}" = "remove" ]; then
  U_OS="$(uname -s)"
  echo "==> Gỡ tunnel chạy ngầm ($U_OS)"
  case "$U_OS" in
    Linux)
      [ "$(id -u)" = 0 ] || { echo "!! Cần root: sudo $0 --uninstall"; exit 1; }
      if command -v systemctl >/dev/null 2>&1 && [ -f /etc/systemd/system/entry-tunnel.service ]; then
        systemctl disable --now entry-tunnel >/dev/null 2>&1 || true
        rm -f /etc/systemd/system/entry-tunnel.service; systemctl daemon-reload
        echo "    ✓ đã gỡ systemd service entry-tunnel"
      fi
      if [ -f /etc/init.d/entry-tunnel ]; then
        rc-service entry-tunnel stop >/dev/null 2>&1 || true
        rc-update del entry-tunnel default >/dev/null 2>&1 || true
        rm -f /etc/init.d/entry-tunnel
        echo "    ✓ đã gỡ OpenRC service entry-tunnel"
      fi
      rm -rf /etc/entry-tunnel && echo "    ✓ đã xoá /etc/entry-tunnel (key + config)"
      ;;
    Darwin)
      PLIST="$HOME/Library/LaunchAgents/vn.entry.tunnel.plist"
      launchctl unload "$PLIST" >/dev/null 2>&1 || true
      rm -f "$PLIST" "$HOME/.ssh/entry_tunnel" "$HOME/.ssh/entry_tunnel.pub"
      echo "    ✓ đã gỡ LaunchAgent + key (vn.entry.tunnel)"
      ;;
    *) echo "!! OS '$U_OS' — gỡ tay, hoặc Windows dùng: tunnel-install.ps1 -Uninstall"; exit 1;;
  esac
  echo "==> Đã gỡ. (SSH key trên tài khoản entry.io.vn KHÔNG bị xoá — vào Dashboard ▸ SSH Keys nếu muốn xoá.)"
  exit 0
fi

SUB="${1:-}"; PORT="${2:-}"; LOCAL_HOST="${3:-localhost}"
TOKEN="${4:-${ENTRY_TOKEN:-}}"    # API token (Dashboard ▸ API Tokens). Có token -> tự đẩy public key lên hệ thống.
if [ -z "$SUB" ] || [ -z "$PORT" ]; then
  echo "Dùng: $0 <subdomain> <port> [local_host] [token]"
  echo "  VD: sudo $0 myapp 3000   ->  https://myapp.entry.io.vn"
  exit 1
fi
echo "$SUB"  | grep -qE '^[a-z0-9][a-z0-9-]{0,30}$' || { echo "!! subdomain chỉ gồm a-z 0-9 và dấu - (vd: myapp)"; exit 1; }
echo "$PORT" | grep -qE '^[0-9]{1,5}$'             || { echo "!! port phải là số (vd: 3000)"; exit 1; }

# In rõ từng bước tool làm (minh bạch cho người dùng).
step(){ echo; echo "==> $*"; }
echo "════════════════════════════════════════════════════════════"
echo " entry.io.vn — cài tunnel chạy ngầm 24/7"
echo "   subdomain = $SUB   ·   port = $PORT   ·   local = $LOCAL_HOST"
echo "   token     = $([ -n "$TOKEN" ] && echo 'CÓ → sẽ tự đẩy public key lên hệ thống' || echo 'không → sẽ in key để thêm tay')"
echo "════════════════════════════════════════════════════════════"

OS="$(uname -s)"
NEW=0

ensure_ssh_linux() {
  command -v ssh >/dev/null 2>&1 && return 0
  echo "==> Cài openssh-client"
  if   command -v apt-get >/dev/null 2>&1; then apt-get update -qq && apt-get install -y -qq openssh-client
  elif command -v dnf     >/dev/null 2>&1; then dnf install -y -q openssh-clients
  elif command -v yum     >/dev/null 2>&1; then yum install -y openssh-clients
  elif command -v pacman  >/dev/null 2>&1; then pacman -Sy --noconfirm openssh
  elif command -v zypper  >/dev/null 2>&1; then zypper -n install openssh
  elif command -v apk     >/dev/null 2>&1; then apk add --no-cache openssh-client
  else echo "!! Cài 'openssh-client' thủ công rồi chạy lại."; exit 1; fi
}

case "$OS" in
# ============================== LINUX ==============================
Linux)
  [ "$(id -u)" = 0 ] || { echo "Linux cần quyền root:  sudo $0 $SUB $PORT"; exit 1; }
  ensure_ssh_linux
  SSH_BIN="$(command -v ssh)"

  CONF_DIR=/etc/entry-tunnel; KEY="$CONF_DIR/id_ed25519"
  mkdir -p "$CONF_DIR"; chmod 700 "$CONF_DIR"
  step "[1/5] SSH key ed25519"
  if [ ! -f "$KEY" ]; then
    ssh-keygen -t ed25519 -N '' -f "$KEY" -C "entry-tunnel@$(hostname)" -q; NEW=1
    echo "    ✓ tạo key mới: $KEY"
  else echo "    • dùng key có sẵn: $KEY"; fi

  # SELinux Enforcing (CentOS/RHEL/Rocky/Fedora) chặn systemd chuyển vào domain ssh_t của
  # ssh-client -> service báo status=203/EXEC. Khắc phục: chạy một BẢN SAO ssh gắn nhãn bin_t
  # (không kích hoạt transition) -> hết 203, SELinux vẫn Enforcing, không đụng ssh hệ thống.
  RUN_SSH="$SSH_BIN"
  if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce 2>/dev/null)" = "Enforcing" ] && command -v chcon >/dev/null 2>&1; then
    if cp -f "$SSH_BIN" "$CONF_DIR/ssh" && chmod 755 "$CONF_DIR/ssh" && chcon -t bin_t "$CONF_DIR/ssh" 2>/dev/null; then
      RUN_SSH="$CONF_DIR/ssh"
      echo "==> SELinux Enforcing -> dùng bản sao ssh (nhãn bin_t) để tránh lỗi 203/EXEC"
    fi
  fi
  ARGS="-N $SSH_OPTS -i $KEY -p $SISH_PORT -R $SUB:80:$LOCAL_HOST:$PORT $SISH_HOST"

  step "[2/5] Dựng service tự-chạy-lại"
  if command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then
    mkdir -p /etc/systemd/system
    cat > /etc/systemd/system/entry-tunnel.service <<UNIT
[Unit]
Description=entry.io.vn tunnel ($SUB -> $LOCAL_HOST:$PORT)
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=$RUN_SSH $ARGS
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
UNIT
    systemctl daemon-reload
    systemctl enable entry-tunnel >/dev/null 2>&1 || true
    START="sudo systemctl restart entry-tunnel"
    STATUS="sudo systemctl status entry-tunnel --no-pager   #  log: journalctl -u entry-tunnel -f"
    OS_LABEL="linux-systemd"; SVC_NAME="entry-tunnel"; SERVICE_FILE="/etc/systemd/system/entry-tunnel.service"
  else
    # Alpine / OpenRC — supervise-daemon tự khởi động lại ssh
    mkdir -p /etc/init.d
    cat > /etc/init.d/entry-tunnel <<RC
#!/sbin/openrc-run
supervisor=supervise-daemon
name="entry-tunnel"
command="$RUN_SSH"
command_args="$ARGS"
respawn_delay=5
respawn_max=0
depend() { need net; }
RC
    chmod +x /etc/init.d/entry-tunnel
    rc-update add entry-tunnel default >/dev/null 2>&1 || true
    START="rc-service entry-tunnel restart"
    STATUS="rc-service entry-tunnel status"
    OS_LABEL="linux-openrc"; SVC_NAME="entry-tunnel"; SERVICE_FILE="/etc/init.d/entry-tunnel"
  fi
  PUB="$(cat "$KEY.pub")"
  INSTALL_DIR="$CONF_DIR"; KEY_PATH="$KEY"
  LAN_IP="$( (hostname -I 2>/dev/null || true) | awk '{print $1}')"
  ;;

# ============================== macOS ==============================
Darwin)
  # ssh có sẵn trên macOS — không cần Homebrew/autossh. launchd KeepAlive tự kết nối lại.
  SSH_BIN="$(command -v ssh)"
  KEY="$HOME/.ssh/entry_tunnel"
  mkdir -p "$HOME/.ssh"; chmod 700 "$HOME/.ssh"
  step "[1/5] SSH key ed25519"
  if [ ! -f "$KEY" ]; then
    ssh-keygen -t ed25519 -N '' -f "$KEY" -C "entry-tunnel@$(hostname)" -q; NEW=1
    echo "    ✓ tạo key mới: $KEY"
  else echo "    • dùng key có sẵn: $KEY"; fi

  step "[2/5] Dựng LaunchAgent (launchd)"
  PLIST="$HOME/Library/LaunchAgents/vn.entry.tunnel.plist"
  mkdir -p "$HOME/Library/LaunchAgents"
  cat > "$PLIST" <<PL
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
  <key>Label</key><string>vn.entry.tunnel</string>
  <key>ProgramArguments</key><array>
    <string>$SSH_BIN</string><string>-N</string>
    <string>-o</string><string>ServerAliveInterval=30</string>
    <string>-o</string><string>ServerAliveCountMax=3</string>
    <string>-o</string><string>ExitOnForwardFailure=yes</string>
    <string>-o</string><string>StrictHostKeyChecking=accept-new</string>
    <string>-i</string><string>$KEY</string>
    <string>-p</string><string>$SISH_PORT</string>
    <string>-R</string><string>$SUB:80:$LOCAL_HOST:$PORT</string>
    <string>$SISH_HOST</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
</dict></plist>
PL
  launchctl unload "$PLIST" 2>/dev/null || true
  START="launchctl load $PLIST"
  STATUS="launchctl list | grep vn.entry.tunnel"
  PUB="$(cat "$KEY.pub")"
  OS_LABEL="macos-launchd"; SVC_NAME="vn.entry.tunnel"; SERVICE_FILE="$PLIST"
  INSTALL_DIR="$HOME/.ssh"; KEY_PATH="$KEY"
  LAN_IP="$(ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || true)"
  ;;

*)
  echo "OS '$OS' chưa hỗ trợ. Windows: dùng tunnel-install.ps1"; exit 1;;
esac

# [3/5] Đăng ký public key — có token thì tự đẩy lên hệ thống, khỏi thêm tay.
step "[3/5] Đăng ký public key"
KEY_PUSHED=0
if [ -n "$TOKEN" ] && command -v curl >/dev/null 2>&1; then
  echo "    → POST $API_BASE/v1/ssh-keys (Bearer token)…"
  CODE=$(curl -s -m 15 -o /dev/null -w '%{http_code}' -X POST "$API_BASE/v1/ssh-keys" \
    -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
    -d "$(printf '{"public_key":"%s","label":"tunnel@%s"}' "$PUB" "$(hostname)")" || echo 000)
  case "$CODE" in
    200) echo "    ✓ đã thêm public key vào tài khoản (tự động)"; KEY_PUSHED=1;;
    409) echo "    ✓ public key đã có sẵn trên tài khoản";           KEY_PUSHED=1;;
    401) echo "    ✗ token sai/không hợp lệ — chuyển sang thêm tay";;
    *)   echo "    ✗ đẩy key thất bại (HTTP $CODE) — chuyển sang thêm tay";;
  esac
fi
if [ "$KEY_PUSHED" != 1 ]; then
  echo "    Chưa có token hợp lệ → THÊM public key này vào Dashboard ▸ SSH Keys ▸ Thêm key:"
  echo
  echo "      $PUB"
fi

# [4/5] Metadata dịch vụ (để sau còn biết đường vào dọn dẹp / kiểm tra). Tắt bằng ENTRY_NO_REGISTER=1.
step "[4/5] Ghi metadata dịch vụ"
if [ "${ENTRY_NO_REGISTER:-0}" != "1" ] && command -v curl >/dev/null 2>&1; then
  META=$(printf '{"service_file":"%s","local_host":"%s","lan_ip":"%s"}' "${SERVICE_FILE:-}" "$LOCAL_HOST" "${LAN_IP:-}")
  if [ -n "$TOKEN" ]; then
    BODY=$(printf '{"hostname":"%s","ip":"%s","os":"%s","service_name":"%s","install_dir":"%s","key_path":"%s","subdomain":"%s","local_port":%s,"meta":%s}' \
      "$(hostname)" "${LAN_IP:-}" "${OS_LABEL:-}" "${SVC_NAME:-}" "${INSTALL_DIR:-}" "${KEY_PATH:-}" "$SUB" "$PORT" "$META")
    curl -fsS -m 10 -X POST "$API_BASE/v1/installs" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d "$BODY" >/dev/null 2>&1 \
      && echo "    ✓ đã ghi (qua token)" || echo "    • bỏ qua"
  else
    BODY=$(printf '{"public_key":"%s","hostname":"%s","os":"%s","service_name":"%s","install_dir":"%s","key_path":"%s","subdomain":"%s","local_port":%s,"meta":%s}' \
      "$PUB" "$(hostname)" "${OS_LABEL:-}" "${SVC_NAME:-}" "${INSTALL_DIR:-}" "${KEY_PATH:-}" "$SUB" "$PORT" "$META")
    curl -fsS -m 10 -X POST "$API_BASE/v1/installs/self-register" -H 'Content-Type: application/json' -d "$BODY" >/dev/null 2>&1 \
      && echo "    ✓ đã ghi (self-register)" || echo "    • pending — webhook sẽ tự ghi khi tunnel kết nối"
  fi
fi

# [5/5] Khởi động — chỉ auto-start khi key đã lên hệ thống (nếu chưa, khởi động sẽ bị 'publickey').
step "[5/5] Khởi động"
if [ "$KEY_PUSHED" = 1 ]; then
  $START >/dev/null 2>&1 && echo "    ✓ tunnel đang chạy" || echo "    • khởi động tay: $START"
else
  echo "    Sau khi đã THÊM public key ở trên, chạy: $START"
fi

echo
echo "════════════════════════════════════════════════════════════"
echo " ✅ XONG   →   https://$SUB.entry.io.vn   →   $LOCAL_HOST:$PORT"
echo "    Kiểm tra: $STATUS"
echo "════════════════════════════════════════════════════════════"
