#!/bin/bash
# SoftEther VPN Auto Installer - SINGLE IP EDITION
# Author: VietHosting (https://viethosting.com)
# Version: 1.0.0 (30.03.2026)
# Supports: CentOS 7 (Vault), Rocky, AlmaLinux, RHEL 8/9/10, Ubuntu 20.04+, Debian 10+
#
# Description:
# Enterprise-grade SoftEther VPN gateway optimized for Single-IP servers.
#
# Key Features:
# - Dual Topology    : SecureNAT (Easy) or Local Bridge (Kernel-level high performance).
# - Class A Routing  : 10.8.30.0/24 subnet to completely eliminate local Wi-Fi conflicts.
# - Smart Routing    : Auto-detects nftables/iptables and dynamic UFW/Firewalld integration.
# - Universal Access : Supports L2TP/IPsec, OpenVPN, MS-SSTP, and SoftEther native clients.
# - SSL Automation   : Optional Let's Encrypt integration for secure SSTP connections.
# - Session Ctrl     : Actively kicks active sessions before revoking users.
# - Log Management   : Advanced logrotate for script logs AND SoftEther internal service logs.
# - Clean Output     : Advanced File Descriptors (FD) to separate console UI from raw logs.
# - Health Checks    : Port listening and /dev/net/tun availability validation.
#
# Usage:
# curl -O https://mirrors.viethosting.com/scripts/softether-installer.sh && bash softether-installer.sh


set -euo pipefail

# --- Configuration ---
SE_URL="https://github.com/SoftEtherVPN/SoftEtherVPN_Stable/releases/download/v4.44-9807-rtm/softether-vpnserver-v4.44-9807-rtm-2025.04.16-linux-x64-64bit.tar.gz"
SE_DIR="/usr/local/vpnserver"
PASS_FILE="/etc/vpnserver/.admin_pass"
LOCK_FILE="/run/lock/softether-install.lock"
LOG_FILE="/var/log/softether-install.log"
OVPN_DIR="/root/vpn_configs"
TMP_OVPN_WORK_DIR=""

# Globals
OS=""
VERSION_MAJOR=""
MAIN_IFACE=""
ADMIN_PASS=""
IPSEC_PSK=""
PUBLIC_IP=""
VPN_DOMAIN=""
CERT_EMAIL=""
USE_LETSENCRYPT="0"
NET_MODE="1"

# --- Advanced FD Logging & Atomic Locks ---
# Keeps terminal clean while logging everything in the background
exec 3>&1 4>&2
exec > >(stdbuf -oL awk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }' 2>/dev/null | tee -a "$LOG_FILE") 2>&1

cleanup() {
    rm -rf /tmp/vpnserver /tmp/softether.tar.gz "$LOCK_FILE" 2>/dev/null || true
    if [[ -n "$TMP_OVPN_WORK_DIR" && -d "$TMP_OVPN_WORK_DIR" ]]; then
        rm -rf "$TMP_OVPN_WORK_DIR" 2>/dev/null || true
    fi
    exec 1>&3 2>&4
}

if [[ "${1:-}" != "--api-add" && "${1:-}" != "--api-revoke" ]]; then
    exec 200>"$LOCK_FILE"
    flock -n 200 || { echo -e "\e[31m⚠️ Error: Another SoftEther provisioning is running.\e[0m" >&3; exit 1; }
    trap cleanup EXIT
else
    API_LOCK="/tmp/softether_api.lock"
    exec 201>"$API_LOCK"
    flock -w 5 201 || { echo '{"status":"error","message":"rate_limited_or_busy"}' >&3; exit 1; }
    trap cleanup EXIT
fi

print_color() {
    local COLOR=${1:-}
    local TEXT=${2:-}
    case $COLOR in
        "green") echo -e "\e[32m${TEXT}\e[0m" >&3 ;;
        "red") echo -e "\e[31m${TEXT}\e[0m" >&3 ;;
        "yellow") echo -e "\e[33m${TEXT}\e[0m" >&3 ;;
        "cyan") echo -e "\e[36m${TEXT}\e[0m" >&3 ;;
        "magenta") echo -e "\e[35m${TEXT}\e[0m" >&3 ;;
        *) echo "${TEXT}" >&3 ;;
    esac
}

check_root() {
    if [[ $EUID -ne 0 ]]; then print_color "red" "❌ Error: Must run as root."; exit 1; fi
}

pre_flight_install_checks() {
    if ! curl -s --max-time 5 https://1.1.1.1 >/dev/null 2>&1; then print_color "red" "❌ Error: No internet access."; exit 1; fi
    if [[ $(df -m / | awk 'NR==2 {print $4}') -lt 500 ]]; then print_color "red" "❌ Error: Not enough disk space!"; exit 1; fi
    
    local ent_avail=$(cat /proc/sys/kernel/random/entropy_avail 2>/dev/null || echo 1000)
    if [[ "$ent_avail" -lt 200 ]]; then print_color "yellow" "⚠️ Warning: Low entropy ($ent_avail). Encryption might be slow."; fi

    for p in 80 443 992 1194 5555; do
        if ss -H -ltnp | awk '{print $4}' | grep -Eq "(:|\.)$p$|\\[::\\]:$p$"; then
            print_color "red" "❌ ERROR: TCP Port $p is heavily bound! Aborting." >&3; exit 1
        fi
    done
}

detect_network_os() {
    source /etc/os-release
    case "${ID:-}" in
        ubuntu|debian) OS="ubuntu" ;;
        centos|almalinux|rocky|rhel|ol) OS="rhel" ;;
        *) print_color "red" "❌ Error: Unsupported OS."; exit 1 ;;
    esac
    VERSION_MAJOR=$(echo "${VERSION_ID:-}" | cut -d. -f1)
    
    MAIN_IFACE=$(ip -o -4 route show to default 2>/dev/null | awk '{print $5}' | head -1)
    if [[ -z "$MAIN_IFACE" ]]; then MAIN_IFACE=$(ip route get 8.8.8.8 2>/dev/null | awk '{print $5}' | head -1); fi
    
    if [[ -z "$MAIN_IFACE" || ! -e "/sys/class/net/$MAIN_IFACE" ]]; then
        print_color "red" "❌ CRITICAL: Cannot detect valid network interface." >&3; exit 1
    fi
    
    PUBLIC_IP=$(curl -4s --max-time 5 icanhazip.com || curl -4s --max-time 5 ifconfig.me || curl -4s --max-time 5 api.ipify.org || true)
    if [[ -z "$PUBLIC_IP" ]]; then PUBLIC_IP=$(ip -4 route get 1 2>/dev/null | awk '{print $7; exit}' || true); fi
    if [[ -z "$PUBLIC_IP" ]]; then print_color "red" "❌ CRITICAL: Cannot detect public IP." >&3; exit 1; fi
}

setup_logrotate() {
    print_color "yellow" "--> Configuring Advanced Logrotate..."
    cat <<EOF > /etc/logrotate.d/softether-vpn
$LOG_FILE
/usr/local/vpnserver/*_log/*.log {
    daily
    rotate 7
    missingok
    notifempty
    compress
    delaycompress
    copytruncate
    create 644 root root
}
EOF
}

install_dependencies() {
    print_color "yellow" "--> Installing Dependencies..."
    if [[ "$OS" == "ubuntu" ]]; then
        export DEBIAN_FRONTEND=noninteractive
        apt-get update -y -q >/dev/null
        apt-get install -y -q build-essential wget curl tar iptables ufw openssl unzip dnsmasq iproute2 gawk haveged certbot cron dnsutils >/dev/null
    elif [[ "$OS" == "rhel" ]]; then
        local DNF_OPT="--setopt=fastestmirror=True --setopt=defaultyes=True"
        if [[ "$VERSION_MAJOR" == "7" ]]; then
            sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* 2>/dev/null || true
            sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* 2>/dev/null || true
            yum install -y epel-release >/dev/null
            yum groupinstall "Development Tools" -y >/dev/null
            yum install -y wget curl tar firewalld iptables-services openssl unzip dnsmasq iproute gawk haveged certbot cronie bind-utils >/dev/null || true
        else
            dnf install $DNF_OPT -y epel-release dnf-plugins-core >/dev/null || true
            if [[ "$VERSION_MAJOR" == "8" ]]; then dnf config-manager --set-enabled powertools >/dev/null || dnf config-manager --set-enabled crb >/dev/null || true; fi
            if [[ "$VERSION_MAJOR" == "9" ]] || [[ "$VERSION_MAJOR" == "10" ]]; then dnf config-manager --set-enabled crb >/dev/null || true; fi
            dnf groupinstall "Development Tools" -y >/dev/null
            dnf install $DNF_OPT -y wget curl tar firewalld iptables-services openssl unzip dnsmasq iproute gawk haveged certbot cronie bind-utils nftables >/dev/null || true
        fi
    fi
    systemctl start haveged >/dev/null 2>&1 || true; systemctl enable haveged >/dev/null 2>&1 || true
    setup_logrotate
}

kernel_tuning_and_firewall() {
    print_color "yellow" "--> Applying SaaS Kernel Tuning & Base Firewall..."
    cat <<EOF > /etc/sysctl.d/99-vpn-tuning.conf
net.ipv4.ip_forward=1
net.core.rmem_max=67108864
net.core.wmem_max=67108864
net.ipv4.tcp_fastopen=3
net.ipv4.tcp_tw_reuse=1
net.ipv4.ip_local_port_range=1024 65535
net.netfilter.nf_conntrack_max=262144
net.core.netdev_max_backlog=5000
net.ipv4.tcp_mtu_probing=1
EOF
    if modprobe tcp_bbr 2>/dev/null && sysctl net.ipv4.tcp_available_congestion_control | grep -q bbr; then
        echo "net.core.default_qdisc=fq" >> /etc/sysctl.d/99-vpn-tuning.conf
        echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.d/99-vpn-tuning.conf
    fi
    sysctl -p /etc/sysctl.d/99-vpn-tuning.conf >/dev/null 2>&1 || true

    if [[ "$OS" == "ubuntu" ]]; then
        sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw 2>/dev/null || true
        sed -i 's|#net/ipv4/ip_forward=1|net/ipv4/ip_forward=1|g' /etc/ufw/sysctl.conf 2>/dev/null || true
        for port in 80/tcp 443/tcp 992/tcp 1194/tcp 1194/udp 5555/tcp 500/udp 4500/udp 67/udp 68/udp; do ufw allow "$port" >/dev/null 2>&1 || true; done
        ufw allow OpenSSH >/dev/null 2>&1 || true
        ufw reload >/dev/null 2>&1 || ufw --force enable >/dev/null 2>&1 || true
    elif [[ "$OS" == "rhel" ]]; then
        systemctl start firewalld && systemctl enable firewalld
        firewall-cmd --zone=public --add-port={80,443,992,1194,5555}/tcp --permanent >/dev/null 2>&1 || true
        firewall-cmd --zone=public --add-port={500,4500,1194,67,68}/udp --permanent >/dev/null 2>&1 || true
        firewall-cmd --zone=public --add-masquerade --permanent >/dev/null 2>&1 || true
        firewall-cmd --reload --quiet >/dev/null 2>&1 || true
    fi
}

provision_letsencrypt() {
    if [[ -n "$VPN_DOMAIN" && -n "$CERT_EMAIL" ]]; then
        if [[ -f "/etc/letsencrypt/live/$VPN_DOMAIN/fullchain.pem" ]]; then
            print_color "green" "✓ Existing Let's Encrypt certificate found. Reusing it." >&3
            USE_LETSENCRYPT="1"; return
        fi

        local DOMAIN_IP=""
        if command -v dig >/dev/null 2>&1; then
            DOMAIN_IP=$(dig @1.1.1.1 +short "$VPN_DOMAIN" A | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | tail -n 1 || true)
        fi
        
        if [[ -z "$DOMAIN_IP" ]]; then
            DOMAIN_IP=$(getent ahostsv4 "$VPN_DOMAIN" | awk 'NR==1 { print $1 }' || true)
        fi

        if [[ "$DOMAIN_IP" != "$PUBLIC_IP" ]]; then
             print_color "red" "⚠️ WARNING: Domain resolves to '$DOMAIN_IP', but server is '$PUBLIC_IP'." >&3
             print_color "yellow" "Skipping Let's Encrypt setup to prevent Rate Limit." >&3; return
        fi

        print_color "yellow" "--> Provisioning Let's Encrypt SSL..." >&3
        local cert_success=0
        for i in {1..3}; do
            if certbot certonly --standalone -d "$VPN_DOMAIN" -m "$CERT_EMAIL" --agree-tos --non-interactive --key-type rsa >/dev/null 2>&1; then
                cert_success=1; break
            fi
            sleep 3
        done

        if [[ "$cert_success" == "1" ]]; then
            USE_LETSENCRYPT="1"
            print_color "green" "✓ Let's Encrypt SSL Issued Successfully (RSA Mode)!" >&3
            
            mkdir -p /usr/local/vpnserver
            
            cat <<'EOF' > /usr/local/vpnserver/renew_cert_hook.sh
#!/bin/bash
source /etc/vpnserver/.admin_pass
timeout 10 /usr/local/vpnserver/vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" <<CMD_EOF >/dev/null 2>&1
ServerCertSet
/etc/letsencrypt/live/$VPN_DOMAIN/fullchain.pem
/etc/letsencrypt/live/$VPN_DOMAIN/privkey.pem
exit
CMD_EOF
EOF
            chmod 700 /usr/local/vpnserver/renew_cert_hook.sh
            echo "0 3 * * * root certbot renew --quiet --deploy-hook /usr/local/vpnserver/renew_cert_hook.sh" > /etc/cron.d/softether-ssl-renew
            chmod 644 /etc/cron.d/softether-ssl-renew
        else
            print_color "red" "⚠️ Let's Encrypt failed. Falling back to Self-Signed." >&3
        fi
    fi
}

install_softether() {
    if [[ -d "$SE_DIR" && -f "${SE_DIR}/vpnserver" ]]; then return; fi
    print_color "yellow" "--> Compiling SoftEther VPN Server (Multi-core)..."
    cd /tmp
    wget --quiet --tries=3 --timeout=10 "$SE_URL" -O softether.tar.gz || { print_color "red" "❌ Download failed"; exit 1; }
    tar -xzf softether.tar.gz && cd vpnserver
    
    printf '1\n1\n1\n' | make -j"$(nproc 2>/dev/null || echo 1)" >/dev/null 2>&1 || { print_color "red" "❌ Compile failed"; exit 1; }
    mkdir -p /usr/local/vpnserver
    cp -a /tmp/vpnserver/* /usr/local/vpnserver/
    rm -rf /tmp/vpnserver
    cd /usr/local/vpnserver/
    chmod 600 vpn_server.config 2>/dev/null || true; chmod 700 vpnserver vpncmd
    
    [[ -x "${SE_DIR}/vpncmd" ]] || { print_color "red" "❌ FATAL: vpncmd missing!"; exit 1; }
    
    cat <<EOF > /etc/systemd/system/vpnserver.service
[Unit]
Description=SoftEther VPN Server
After=network.target auditd.service

[Service]
Type=forking
ExecStart=/usr/local/vpnserver/vpnserver start
ExecStop=/usr/local/vpnserver/vpnserver stop
KillMode=process
Restart=always
RestartSec=3
LimitNOFILE=65535
LimitNPROC=infinity
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWriteDirectories=/usr/local/vpnserver

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload; systemctl enable vpnserver >/dev/null 2>&1 || true; systemctl start vpnserver
    
    print_color "yellow" "--> Waiting for SoftEther Core Hubs to initialize..."
    local se_ready=0
    for i in {1..15}; do
        if timeout 5 ./vpncmd 127.0.0.1 /SERVER /CMD ListenerList >/dev/null 2>&1; then se_ready=1; break; fi
        sleep 1
    done

    if [[ "$se_ready" -eq 0 ]]; then
        print_color "red" "❌ VPN service crashed or timeout! Check logs:" >&3
        journalctl -u vpnserver --no-pager -n 20 >&3; exit 1
    fi
}

configure_network_mode() {
    echo "=================================================================" >&3
    print_color "cyan" "SELECT NETWORK TOPOLOGY (PERFORMANCE OPTIMIZATION)" >&3
    echo "1. SecureNAT (Default) - Standard performance, easy setup." >&3
    echo "2. Local Bridge (tap)  - Kernel-level high performance (Native NAT)." >&3
    echo "=================================================================" >&3
    
    echo -e -n "\e[33mEnter your choice [1 or 2, default: 1]: \e[0m" >&3
    read -r USER_MODE <&3 || USER_MODE=""
    NET_MODE=${USER_MODE:-1}
    
    if [[ "$NET_MODE" == "2" ]]; then
        modprobe tun 2>/dev/null || true
        if [[ ! -c /dev/net/tun ]]; then 
            print_color "red" "❌ TUN device not available (/dev/net/tun missing). SecureNAT fallback." >&3
            NET_MODE="1"
            return
        fi
        print_color "yellow" "--> Configuring Local Bridge & dnsmasq..." >&3
        
        if systemctl is-active systemd-resolved >/dev/null 2>&1; then
            mkdir -p /etc/systemd/resolved.conf.d
            echo -e "[Resolve]\nDNSStubListener=no" > /etc/systemd/resolved.conf.d/dnsmasq-fix.conf
            systemctl restart systemd-resolved; sleep 1
            if [[ -L /etc/resolv.conf && $(readlink /etc/resolv.conf) == *"stub-resolv.conf"* ]]; then
                rm -f /etc/resolv.conf; ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
            fi
        fi
        
        if [[ -f /etc/dnsmasq.conf ]]; then
            sed -i 's/^[[:space:]]*bind-interfaces/#bind-interfaces/g' /etc/dnsmasq.conf 2>/dev/null || true
        fi
        
        cat <<EOF > /etc/dnsmasq.d/softether.conf
interface=tap_soft
bind-dynamic
dhcp-range=tap_soft,10.8.30.10,10.8.30.200,12h
dhcp-option=tap_soft,3,10.8.30.1
dhcp-option=tap_soft,6,1.1.1.1,1.0.0.1
EOF
        
        systemctl enable dnsmasq >/dev/null 2>&1 || true
        systemctl restart dnsmasq >/dev/null 2>&1 || true

        cat <<'EOF' > /usr/local/vpnserver/apply_nat.sh
#!/bin/bash
source /etc/os-release
VERSION_MAJOR=$(echo "${VERSION_ID:-}" | cut -d. -f1)
MAIN_IFACE=$(ip -o -4 route show to default 2>/dev/null | awk '{print $5}' | head -1)

if command -v firewall-cmd >/dev/null 2>&1; then
    firewall-cmd --zone=trusted --add-interface=tap_soft --quiet 2>/dev/null || true
fi

if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
    iptables -t nat -C POSTROUTING -s 10.8.30.0/24 -o "$MAIN_IFACE" -j MASQUERADE 2>/dev/null || iptables -t nat -I POSTROUTING 1 -s 10.8.30.0/24 -o "$MAIN_IFACE" -j MASQUERADE
    iptables -C FORWARD -i tap_soft -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -i tap_soft -j ACCEPT
    iptables -C FORWARD -o tap_soft -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -o tap_soft -j ACCEPT
    
    if command -v ufw >/dev/null 2>&1; then
        if ! grep -q "# SOFTETHER_NAT" /etc/ufw/before.rules; then
            if ! grep -q "*nat" /etc/ufw/before.rules; then
                sed -i '1s;^;# SOFTETHER_NAT\n*nat\n:POSTROUTING ACCEPT [0:0]\nCOMMIT\n\n;' /etc/ufw/before.rules
            else
                sed -i '/^\*nat/i # SOFTETHER_NAT' /etc/ufw/before.rules
            fi
        fi
        if ! grep -q "10.8.30.0/24" /etc/ufw/before.rules; then
            sed -i "/^:POSTROUTING ACCEPT/a -A POSTROUTING -s 10.8.30.0/24 -o $MAIN_IFACE -j MASQUERADE" /etc/ufw/before.rules
            ufw reload >/dev/null 2>&1 || true
        fi
        ufw allow in on tap_soft >/dev/null 2>&1 || true
        ufw allow out on tap_soft >/dev/null 2>&1 || true
        ufw route allow in on tap_soft out on "$MAIN_IFACE" >/dev/null 2>&1 || true
        ufw route allow out on "$MAIN_IFACE" in on tap_soft >/dev/null 2>&1 || true
    fi
elif [[ "$ID" =~ (centos|almalinux|rocky|rhel|ol) ]] && [[ "$VERSION_MAJOR" == "7" ]]; then
    iptables -t nat -C POSTROUTING -s 10.8.30.0/24 -o "$MAIN_IFACE" -j MASQUERADE 2>/dev/null || iptables -t nat -I POSTROUTING 1 -s 10.8.30.0/24 -o "$MAIN_IFACE" -j MASQUERADE
    iptables -C FORWARD -i tap_soft -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -i tap_soft -j ACCEPT
    iptables -C FORWARD -o tap_soft -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -o tap_soft -j ACCEPT
else
    nft add table ip se_nat 2>/dev/null || true
    nft add chain ip se_nat postrouting '{ type nat hook postrouting priority 100; policy accept; }' 2>/dev/null || true
    nft list chain ip se_nat postrouting 2>/dev/null | grep -q "10.8.30.0/24" || \
    nft add rule ip se_nat postrouting ip saddr 10.8.30.0/24 oifname "$MAIN_IFACE" masquerade 2>/dev/null || true
fi
EOF
        chmod +x /usr/local/vpnserver/apply_nat.sh

        cat <<'EOF' > /usr/local/vpnserver/setup_bridge.sh
#!/bin/bash
MAIN_IFACE=$(ip -o -4 route show to default 2>/dev/null | awk '{print $5}' | head -1)
tap_ready=0
for i in {1..20}; do
    if /sbin/ip link show tap_soft >/dev/null 2>&1; then tap_ready=1; break; fi
    sleep 1
done

if [[ "$tap_ready" -eq 1 ]]; then
    command -v nmcli >/dev/null 2>&1 && nmcli dev set tap_soft managed no 2>/dev/null || true
    /sbin/ip link set dev tap_soft up
    sleep 1
    /sbin/ip addr flush dev tap_soft 2>/dev/null
    /sbin/ip addr add 10.8.30.1/24 dev tap_soft
fi

if dnsmasq --test >/dev/null 2>&1; then
    systemctl restart dnsmasq >/dev/null 2>&1 || echo "⚠️ Warning: dnsmasq restart failed"
fi
/bin/bash /usr/local/vpnserver/apply_nat.sh >/dev/null 2>&1
EOF
        chmod +x /usr/local/vpnserver/setup_bridge.sh
        
cat <<EOF > /etc/systemd/system/vpnserver-bridge.service
[Unit]
Description=SoftEther Local Bridge Helper
Requires=vpnserver.service
After=vpnserver.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash /usr/local/vpnserver/setup_bridge.sh

[Install]
WantedBy=multi-user.target
EOF
        systemctl daemon-reload; systemctl enable vpnserver-bridge >/dev/null 2>&1 || true
    fi
}

configure_softether() {
    print_color "yellow" "--> Applying SaaS Cipher & Multi-Protocol Settings..."
    SE_ADMIN_PASS=$(openssl rand -hex 10)
    SE_PSK=$(openssl rand -hex 6)
    
    mkdir -p /etc/vpnserver
    echo "ADMIN_PASS=\"$SE_ADMIN_PASS\"" > "$PASS_FILE"
    echo "IPSEC_PSK=\"$SE_PSK\"" >> "$PASS_FILE"
    echo "VPN_DOMAIN=\"${VPN_DOMAIN:-}\"" >> "$PASS_FILE"
    echo "USE_LETSENCRYPT=\"${USE_LETSENCRYPT:-0}\"" >> "$PASS_FILE"
    chmod 600 "$PASS_FILE"; chown root:root "$PASS_FILE"
    
    local CERT_HOST="${VPN_DOMAIN:-$PUBLIC_IP}"
    
    local SE_CMD_FILE
    SE_CMD_FILE=$(mktemp /tmp/se_setup.XXXXXX.cmd)
    chmod 600 "$SE_CMD_FILE"
    
    echo "ServerPasswordSet ${SE_ADMIN_PASS}" > "$SE_CMD_FILE"

    cat <<EOF >> "$SE_CMD_FILE"
IPsecEnable /L2TP:yes /L2TPRAW:yes /ETHERIP:yes /PSK:${SE_PSK} /DEFAULT:DEFAULT
OpenVpnEnable yes /PORTS:1194
SstpEnable yes
ServerCipherSet ECDHE-RSA-AES256-GCM-SHA384
Hub DEFAULT
EOF

	if [[ "$NET_MODE" == "2" ]]; then echo "BridgeCreate DEFAULT /DEVICE:soft /TAP:yes" >> "$SE_CMD_FILE"
    else
        echo "SecureNatEnable" >> "$SE_CMD_FILE"
        echo "SecureNatHostSet /MAC:none /IP:10.8.30.1 /MASK:255.255.255.0" >> "$SE_CMD_FILE"
        echo "DhcpSet /START:10.8.30.10 /END:10.8.30.200 /MASK:255.255.255.0 /EXPIRE:7200 /GW:10.8.30.1 /DNS:1.1.1.1 /DNS2:1.0.0.1 /DOMAIN:none /LOG:yes" >> "$SE_CMD_FILE"
    fi

    cd ${SE_DIR}
    local cmd_success=0
    for i in {1..3}; do
        if timeout 30 ./vpncmd 127.0.0.1 /SERVER /IN:"$SE_CMD_FILE" >/dev/null 2>&1; then cmd_success=1; break; fi
        sleep 2
    done
    rm -f "$SE_CMD_FILE"

    if [[ "$cmd_success" -eq 0 ]]; then print_color "red" "❌ FATAL: SoftEther core configuration failed!" >&3; exit 1; fi
    
    if [[ "${USE_LETSENCRYPT:-0}" == "1" ]]; then
        timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$SE_ADMIN_PASS" <<EOF >/dev/null 2>&1 || true
ServerCertSet
/etc/letsencrypt/live/$VPN_DOMAIN/fullchain.pem
/etc/letsencrypt/live/$VPN_DOMAIN/privkey.pem
exit
EOF
    else
        timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$SE_ADMIN_PASS" <<EOF >/dev/null 2>&1 || true
ServerCertRegenerate
${CERT_HOST}
exit
EOF
    fi

    if [[ "$NET_MODE" == "2" ]]; then systemctl start vpnserver-bridge >/dev/null 2>&1 || true; fi

    print_color "yellow" "--> Running post-installation health checks..." >&3
    if ss -H -ltnp | awk '{print $4}' | grep -qE "(:|\.)443$|\\[::\\]:443$"; then
         print_color "green" "✓ Port 443 is actively listening." >&3
    else
         print_color "yellow" "⚠️ Port 443 is not listening! Ensure the service started correctly." >&3
    fi
}

load_credentials() {
    if [[ ! -f "$PASS_FILE" ]]; then return 1; fi
    ADMIN_PASS=$(grep '^ADMIN_PASS=' "$PASS_FILE" | cut -d'"' -f2 || true)
    IPSEC_PSK=$(grep '^IPSEC_PSK=' "$PASS_FILE" | cut -d'"' -f2 || true)
    VPN_DOMAIN=$(grep '^VPN_DOMAIN=' "$PASS_FILE" | cut -d'"' -f2 || true)
    USE_LETSENCRYPT=$(grep '^USE_LETSENCRYPT=' "$PASS_FILE" | cut -d'"' -f2 || true)
    if [[ -z "$USE_LETSENCRYPT" ]]; then USE_LETSENCRYPT="0"; fi
    if [[ -z "$ADMIN_PASS" || -z "$IPSEC_PSK" ]]; then return 1; fi
}

get_user_list() {
    local RAW_OUTPUT
    RAW_OUTPUT=$(timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:DEFAULT /CMD UserList 2>/dev/null || true)
    
    if echo "$RAW_OUTPUT" | grep -qEi 'Item[[:space:]]*[|│][[:space:]]*Value'; then
        echo "$RAW_OUTPUT" | grep -Ei 'User Name[[:space:]]*[|│]' | awk -F '[|│]' '{print $2}' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
    else
        echo "$RAW_OUTPUT" | grep '[|│]' | grep -vi 'User Name' | awk -F '[|│]' '{print $1}' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | sed '/^$/d'
    fi
}

export_openvpn_config() {
    local USER=$1
    TMP_OVPN_WORK_DIR=$(mktemp -d /tmp/ovpn.XXXXXX)
    mkdir -p "$OVPN_DIR"
    cd ${SE_DIR}
    
    local ZIP_FILE="/tmp/se_ovpn_${USER}_$$.zip"
    
    timeout 30 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" <<EOF >/dev/null 2>&1 || true
OpenVpnMakeConfig
${ZIP_FILE}
exit
EOF
    
    if [[ -f "$ZIP_FILE" ]]; then
        unzip -q -o "$ZIP_FILE" -d "$TMP_OVPN_WORK_DIR/" >/dev/null 2>&1 || true

        local OVPN_FILE_TMP
        OVPN_FILE_TMP=$(find "$TMP_OVPN_WORK_DIR" -maxdepth 1 -iname "*l3*.ovpn" | head -n 1)
        if [[ -z "$OVPN_FILE_TMP" ]]; then
            OVPN_FILE_TMP=$(find "$TMP_OVPN_WORK_DIR" -maxdepth 1 -iname "*.ovpn" | grep -vi "l2" | head -n 1 || true)
        fi
        
        if [[ -n "$OVPN_FILE_TMP" && -f "$OVPN_FILE_TMP" ]]; then
            local FINAL_OVPN="${OVPN_DIR}/${USER}_openvpn.ovpn"
            mv "$OVPN_FILE_TMP" "$FINAL_OVPN"
            sed -i "s/^remote .*/remote ${VPN_DOMAIN:-$PUBLIC_IP} 1194/" "$FINAL_OVPN"
            echo -e "\n# SaaS Optimized Settings\nauth-nocache\nresolv-retry infinite\nkeepalive 10 120\nredirect-gateway def1 bypass-dhcp" >> "$FINAL_OVPN"
            
            rm -f "$ZIP_FILE"; rm -rf "$TMP_OVPN_WORK_DIR"; TMP_OVPN_WORK_DIR=""
            echo "$FINAL_OVPN"
            return 0
        fi
    fi
    
    rm -f "$ZIP_FILE" 2>/dev/null || true
    rm -rf "$TMP_OVPN_WORK_DIR" 2>/dev/null || true; TMP_OVPN_WORK_DIR=""
    echo "N/A"
}

interactive_create_user() {
    load_credentials || return 1
    cd ${SE_DIR}
    
    local USERNAME=""
    local RAND_U="user_$(openssl rand -hex 2)"
    while true; do
        echo -e -n "\n\e[33mEnter new VPN username [Default: $RAND_U]: \e[0m" >&3
        read -r RAW_USER <&3 || RAW_USER=""
        USERNAME=$(echo "${RAW_USER:-$RAND_U}" | tr -cd 'a-zA-Z0-9_-' | cut -c1-32)
        
        local CURRENT_USERS
        CURRENT_USERS=$(get_user_list || true)
        if echo "$CURRENT_USERS" | grep -qw "$USERNAME"; then
             print_color "red" "❌ ERROR: User '${USERNAME}' already exists! Please choose another name." >&3
        else
             break
        fi
    done
    
    local USERPASS=""
    while true; do
        echo -e -n "\e[33mEnter password for $USERNAME (Leave blank to auto-generate): \e[0m" >&3
        read -rs USERPASS <&3 || USERPASS=""
        echo >&3
        
        if [[ -z "$USERPASS" ]]; then
            USERPASS=$(openssl rand -hex 8)
            break
        fi
        
        echo -e -n "\e[33mConfirm password for $USERNAME: \e[0m" >&3
        read -rs USERPASS_CONFIRM <&3 || USERPASS_CONFIRM=""
        echo >&3
        
        if [[ "$USERPASS" == "$USERPASS_CONFIRM" ]]; then
            break
        else
            print_color "red" "❌ Passwords do not match! Please try again.\n" >&3
        fi
    done

    print_color "cyan" "--> Creating user securely..." >&3
    
    local USER_CMD_FILE
    USER_CMD_FILE=$(mktemp /tmp/user_setup.XXXXXX.cmd)
    chmod 600 "$USER_CMD_FILE"
    
    cat <<EOF > "$USER_CMD_FILE"
UserCreate $USERNAME /GROUP:none /REALNAME:none /NOTE:none
UserPasswordSet $USERNAME /PASSWORD:$USERPASS
EOF

    if ! timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:DEFAULT /IN:"$USER_CMD_FILE" >/dev/null 2>&1; then
        print_color "red" "❌ ERROR: Cannot create user!" >&3; rm -f "$USER_CMD_FILE"; return 1
    fi
    rm -f "$USER_CMD_FILE"
    
    sleep 3
    OVPN_PATH=$(export_openvpn_config "$USERNAME")

    echo "" >&3
    echo "=================================================================" >&3
    print_color "green" " 🎉 VPN USER [${USERNAME}] CREATED SUCCESSFULLY! " >&3
    echo "=================================================================" >&3

    echo -e "\e[36m[1] L2TP/IPsec (Popular for Windows, Mac, iOS, legacy Android)\e[0m\n" >&3
    echo -e "    - Server Address           : \e[32m${VPN_DOMAIN:-$PUBLIC_IP}\e[0m" >&3
    echo -e "    - VPN Type                 : \e[32mL2TP/IPsec with pre-shared key\e[0m" >&3
    echo -e "    - Pre-shared Key (PSK)     : \e[32m${IPSEC_PSK}\e[0m" >&3
    echo -e "    - Username                 : \e[32m${USERNAME}\e[0m" >&3
    echo -e "    - Password                 : \e[32m${USERPASS}\e[0m\n\n" >&3

    echo -e "\e[36m[2] OpenVPN (Highly stable, auto-reconnects on 4G/Wifi drop)\e[0m\n" >&3
    if [[ "$OVPN_PATH" != "N/A" ]]; then
        echo -e "    - Config File (.ovpn)      : \e[32m${OVPN_PATH}\e[0m" >&3
        echo -e "    - Step 1: Download the .ovpn file above to your device." >&3
        echo -e "    - Step 2: Install 'OpenVPN Connect' app and import the file." >&3
        echo -e "    - Step 3: Enter credentials (Username: \e[32m${USERNAME}\e[0m / Password: \e[32m${USERPASS}\e[0m) to connect.\n\n" >&3
    else
        echo -e "    - Status                   : \e[31mExport failed. Please export manually via SoftEther GUI.\e[0m\n\n" >&3
    fi

    echo -e "\e[36m[3] MS-SSTP (Excellent for bypassing strict firewalls on Windows)\e[0m\n" >&3
    if [[ "${USE_LETSENCRYPT:-0}" == "1" ]]; then
        echo -e "    - Server Address           : \e[32m${VPN_DOMAIN}\e[0m" >&3
        echo -e "    - VPN Type                 : \e[32mSecure Socket Tunneling Protocol (SSTP)\e[0m" >&3
        echo -e "    - Username                 : \e[32m${USERNAME}\e[0m" >&3
        echo -e "    - Password                 : \e[32m${USERPASS}\e[0m\n\n" >&3
    else
        echo -e "    - Status                   : \e[33mWarning (No Domain/SSL Certificate detected)\e[0m" >&3
        echo -e "    - Advice                   : Windows will block SSTP connections without a valid SSL." >&3
        echo -e "                               : Please use L2TP/IPsec or OpenVPN instead.\n\n" >&3
    fi

    echo -e "\e[36m[4] SoftEther Native (Maximum performance, DPI bypass)\e[0m\n" >&3
    echo -e "    - Step 1: Download 'SoftEther VPN Client' from the official site." >&3
    echo -e "    - Step 2: Create a new connection and enter the following:" >&3
    echo -e "          - Setting Name: <Any Name>" >&3
    echo -e "          - Destination VPN Server:" >&3
    echo -e "              Host: \e[32m${VPN_DOMAIN:-$PUBLIC_IP}\e[0m | Port: \e[32m443\e[0m (or 5555)." >&3
    echo -e "              Select Virtual Hub as 'DEFAULT'." >&3
    echo -e "          - User Authentication Setting: " >&3
    echo -e "              Auth Type: Standard Password Authentication" >&3
    echo -e "              Username: \e[32m${USERNAME}\e[0m" >&3
    echo -e "              Password: \e[32m${USERPASS}\e[0m\n" >&3
    echo "=================================================================" >&3
}

list_users() {
    load_credentials || return 1
    cd ${SE_DIR}
    
    local USER_LIST=""
    USER_LIST=$(get_user_list || true)

    if [[ -z "$USER_LIST" ]]; then
        print_color "yellow" "No users found in the system." >&3
        return 0
    fi

    echo "=================================================================" >&3
    echo -e "\e[36mID\tUSERNAME\e[0m" >&3
    echo "-----------------------------------------------------------------" >&3
    mapfile -t USERS_ARRAY <<< "$USER_LIST"
    for i in "${!USERS_ARRAY[@]}"; do
        printf "%-8s%s\n" "[$((i+1))]" "${USERS_ARRAY[$i]}" >&3
    done
    echo "=================================================================" >&3
}

revoke_user() {
    load_credentials || return 1
    cd ${SE_DIR}
    
    local USER_LIST=""
    USER_LIST=$(get_user_list || true)

    if [[ -z "$USER_LIST" ]]; then
        print_color "yellow" "No users found to revoke." >&3
        return 1
    fi

    echo "=================================================================" >&3
    echo -e "\e[36mID\tUSERNAME\e[0m" >&3
    echo "-----------------------------------------------------------------" >&3
    mapfile -t USERS_ARRAY <<< "$USER_LIST"
    for i in "${!USERS_ARRAY[@]}"; do
        printf "%-8s%s\n" "[$((i+1))]" "${USERS_ARRAY[$i]}" >&3
    done
    echo "=================================================================" >&3
    
    echo -e -n "\e[33mEnter the ID of the user to revoke (or press Enter to cancel): \e[0m" >&3
    read -r USER_ID <&3 || USER_ID=""
    if [[ -z "$USER_ID" ]]; then return 0; fi

    if ! [[ "$USER_ID" =~ ^[0-9]+$ ]] || [[ "$USER_ID" -lt 1 ]] || [[ "$USER_ID" -gt ${#USERS_ARRAY[@]} ]]; then
        print_color "red" "❌ Invalid ID selection." >&3
        return 1
    fi

    local USERNAME="${USERS_ARRAY[$((USER_ID-1))]}"
    echo -e -n "\e[31mAre you sure you want to completely revoke user '${USERNAME}'? (y/N): \e[0m" >&3
    read -r CONFIRM <&3 || CONFIRM=""
    if [[ "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]]; then
        print_color "yellow" "Revocation cancelled." >&3
        return 0
    fi
    
    print_color "yellow" "--> Kicking active sessions for user [${USERNAME}]..." >&3
    local SESSIONS=$(timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:DEFAULT /CMD SessionList 2>/dev/null | awk -F '[|│]' -v u="$USERNAME" '{
        if (NF >= 4) {
            uname = $4; gsub(/^[ \t]+|[ \t]+$/, "", uname);
            if (uname == u) {
                sname = $1; gsub(/^[ \t]+|[ \t]+$/, "", sname);
                print sname;
            }
        }
    }' || true)
    
    for s in $SESSIONS; do
        if [[ -n "$s" && "$s" != "Session Name" ]]; then
            timeout 5 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:DEFAULT /CMD SessionDisconnect "$s" >/dev/null 2>&1 || true
        fi
    done

    timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:DEFAULT /CMD UserDelete "$USERNAME" >/dev/null 2>&1 || true
    rm -f "${OVPN_DIR}/${USERNAME}_openvpn.ovpn" 2>/dev/null || true
    print_color "green" "✓ User [${USERNAME}] has been successfully deleted." >&3
}

remove_softether() {
    print_color "red" "WARNING: COMPLETELY REMOVE SOFTETHER VPN SERVER?" >&3
    echo -e -n "\e[31mType 'y' to confirm: \e[0m" >&3
    read -r CONFIRM <&3 || CONFIRM=""
    if [[ "$CONFIRM" != "y" ]] && [[ "$CONFIRM" != "Y" ]]; then exit 0; fi

    systemctl stop vpnserver 2>/dev/null || true; systemctl disable vpnserver 2>/dev/null || true
    systemctl stop vpnserver-bridge 2>/dev/null || true; systemctl disable vpnserver-bridge 2>/dev/null || true
    rm -rf /usr/local/vpnserver /etc/systemd/system/vpnserver.service /etc/systemd/system/vpnserver-bridge.service /etc/vpnserver
    rm -f /etc/sysctl.d/99-vpn-tuning.conf /etc/dnsmasq.d/softether.conf /etc/cron.d/softether-ssl-renew
    
    if command -v nft >/dev/null 2>&1; then nft delete table ip se_nat 2>/dev/null || true; fi
    iptables -t nat -D POSTROUTING -s 10.8.30.0/24 -o "$MAIN_IFACE" -j MASQUERADE 2>/dev/null || true
    
    if command -v ufw >/dev/null 2>&1; then
        sed -i '/# SOFTETHER_NAT/d' /etc/ufw/before.rules 2>/dev/null || true
        grep -vw "\-A POSTROUTING -s 10.8.30.0/24 " /etc/ufw/before.rules > /tmp/ufw_clean.tmp 2>/dev/null && mv /tmp/ufw_clean.tmp /etc/ufw/before.rules 2>/dev/null || true
        ufw reload >/dev/null 2>&1 || true
    fi

    rm -rf "$OVPN_DIR"
    if [[ -n "${VPN_DOMAIN:-}" ]]; then
        certbot delete --cert-name "$VPN_DOMAIN" --non-interactive 2>/dev/null || rm -rf /etc/letsencrypt/{live,archive,renewal}/"$VPN_DOMAIN"* 2>/dev/null || true
    fi
    
    systemctl restart dnsmasq 2>/dev/null || true
    systemctl daemon-reload; sysctl --system >/dev/null 2>&1
    
    if [[ "$OS" == "rhel" ]]; then
        firewall-cmd --permanent --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.8.30.0/24 -j MASQUERADE >/dev/null 2>&1 || true
        firewall-cmd --zone=trusted --remove-source=10.8.30.0/24 --permanent >/dev/null 2>&1 || true
        firewall-cmd --zone=trusted --remove-interface=tap_soft --permanent >/dev/null 2>&1 || true
        firewall-cmd --reload >/dev/null 2>&1 || true
    fi

    print_color "green" "✓ SoftEther VPN and configs completely removed!" >&3
    exit 0
}

# --- ENTRY POINT & SAAS ROUTING ---
check_root

if [[ -f /usr/local/vpnserver/vpnserver ]]; then
    detect_network_os
    print_color "green" "SoftEther VPN Server Cross-OS is already installed." >&3
    echo "1. Add a new VPN User" >&3
    echo "2. Revoke a VPN User" >&3
    echo "3. List VPN Users" >&3
    echo "4. Remove SoftEther Completely" >&3
    echo "5. Exit" >&3
    echo -e -n "\e[33mSelect an option [1-5]: \e[0m" >&3
    read -r choice <&3 || choice=""
    case ${choice:-} in
        1) interactive_create_user ;;
        2) revoke_user ;;
        3) list_users ;;
        4) remove_softether ;;
        *) exit 0 ;;
    esac
else
    pre_flight_install_checks
    detect_network_os
    
    print_color "yellow" "Starting SoftEther VPN Server Deployment..." >&3
    echo "=================================================================" >&3
    echo -e -n "\e[33mEnter a Domain Name for SSL (Optional, press Enter to use IP): \e[0m" >&3
    read -r VPN_DOMAIN <&3 || VPN_DOMAIN=""
    if [[ -n "$VPN_DOMAIN" ]]; then
        echo -e -n "\e[33mEnter Email for Let's Encrypt SSL (Required for Domain): \e[0m" >&3
        read -r CERT_EMAIL <&3 || CERT_EMAIL=""
    fi
    echo "=================================================================" >&3
    
    install_dependencies
    kernel_tuning_and_firewall
    provision_letsencrypt
    install_softether
    configure_network_mode
    configure_softether
    
    print_color "green" "✓ Core Engine installed and listening successfully!" >&3
    interactive_create_user
fi