#!/bin/bash
# SoftEther VPN Auto Installer - MULTI-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 with fully automated deployment.
# Implements a 1:1 SNAT Multi-IP architecture to map private subnets to dedicated public IPs
# via isolated Virtual Hubs.
#
# Key Features:
# - Massive Scalability : Supports up to 254 isolated Virtual Hubs (10.8.x.0/24 subnet architecture).
# - Identity Isolation  : Dedicated Exit IP per Hub with automatic L2/L3 bridging and DHCP (dnsmasq).
# - 1:1 NAT Mapping     : Each subnet is mapped to a unique public IP via SNAT routing.
# - Universal Access    : Supports L2TP/IPsec, OpenVPN, MS-SSTP, and SoftEther native clients.
# - Smart Routing Engine: Auto-detects nftables/iptables and integrates with UFW/Firewalld.
# - SSL Automation      : Optional Let's Encrypt integration for secure SSTP connections.
# - SaaS-Ready CLI      : Interactive user management, auto .ovpn export, and garbage collection.
# - Clean Output        : Advanced File Descriptors (FD) to separate console UI from raw logs.
# - Log Management      : Advanced logrotate for script logs AND SoftEther internal service logs.
#
# Usage:
# curl -O https://mirrors.viethosting.com/scripts/softether-installer-multi-ip.sh && softether-installer-multi-ip.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"
IP_MAP_FILE="/etc/vpnserver/ip_map.txt"
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=""
VPN_DOMAIN=""
CERT_EMAIL=""
USE_LETSENCRYPT="0"

# --- Advanced FD Logging ---
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 ;;
        *) echo "${TEXT}" >&3 ;;
    esac
}

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

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."; exit 1; fi
}

get_public_ips() {
    ip -4 addr show dev "$MAIN_IFACE" scope global | awk '/inet / {print $2}' | cut -d/ -f1
}

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
    
    modprobe tun 2>/dev/null || true
    if [[ ! -c /dev/net/tun ]]; then print_color "red" "❌ ERROR: TUN/TAP device not available (/dev/net/tun missing). Multi-IP requires kernel tap support." >&3; exit 1; fi

    local AVAIL_IPS
    mapfile -t AVAIL_IPS < <(get_public_ips)
    if [[ ${#AVAIL_IPS[@]} -eq 0 ]]; then print_color "red" "❌ ERROR: No Routable IPs detected on $MAIN_IFACE!"; exit 1; 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 bound! Aborting."; exit 1; fi
    done
}

setup_logrotate() {
    print_color "yellow" "--> Configuring Advanced Logrotate..." >&3
    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 Enterprise Kernel Tuning & 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
        print_color "yellow" "--> Provisioning Let's Encrypt SSL..."
        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!"
            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."
        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

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload; systemctl enable vpnserver >/dev/null 2>&1 || true; systemctl start vpnserver
    
    local se_ready=0
    for i in {1..30}; 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 delayed!"; exit 1; fi
}

configure_base_bridge() {
    print_color "yellow" "--> Configuring Base Multi-IP Bridge Services..."
    mkdir -p /etc/vpnserver
    touch "$IP_MAP_FILE"
    chmod 600 "$IP_MAP_FILE"
    
    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
        if ! grep -q "^conf-dir=/etc/dnsmasq.d" /etc/dnsmasq.conf 2>/dev/null; then
            echo "conf-dir=/etc/dnsmasq.d,.rpmnew,.rpmsave,.rpmorig" >> /etc/dnsmasq.conf
        fi
    fi
    
    systemctl enable 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)

{
flock -s 202
if command -v nft >/dev/null 2>&1 && [[ "$ID" != "ubuntu" && "$ID" != "debian" && "$VERSION_MAJOR" != "7" ]]; then
    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
    while read -r HUB TAP SUBNET PUB_IP; do
        [[ -z "$SUBNET" ]] && continue
        nft list chain ip se_nat postrouting 2>/dev/null | grep -q "${SUBNET}.0/24" || \
        nft add rule ip se_nat postrouting ip saddr "${SUBNET}.0/24" oifname "$MAIN_IFACE" snat to "$PUB_IP" 2>/dev/null || true
    done < /etc/vpnserver/ip_map.txt
else
    while read -r HUB TAP SUBNET PUB_IP; do
        [[ -z "$SUBNET" ]] && continue
        iptables -t nat -C POSTROUTING -s "${SUBNET}.0/24" -o "$MAIN_IFACE" -j SNAT --to-source "$PUB_IP" 2>/dev/null || iptables -t nat -I POSTROUTING 1 -s "${SUBNET}.0/24" -o "$MAIN_IFACE" -j SNAT --to-source "$PUB_IP"
        iptables -C FORWARD -i "$TAP" -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -i "$TAP" -j ACCEPT
        iptables -C FORWARD -o "$TAP" -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -o "$TAP" -j ACCEPT
        
        if command -v ufw >/dev/null 2>&1; then
            if ! grep -q "*nat" /etc/ufw/before.rules; then
                sed -i '1s;^;*nat\n:POSTROUTING ACCEPT [0:0]\nCOMMIT\n\n;' /etc/ufw/before.rules
            fi
            if ! grep -q "${SUBNET}.0/24" /etc/ufw/before.rules; then
                sed -i "/^:POSTROUTING ACCEPT/a -A POSTROUTING -s ${SUBNET}.0/24 -o $MAIN_IFACE -j SNAT --to-source $PUB_IP" /etc/ufw/before.rules
                ufw reload >/dev/null 2>&1 || true
            fi
        fi
    done < /etc/vpnserver/ip_map.txt
fi
} 202>"/etc/vpnserver/ip_map.txt.lock"
EOF
    chmod +x /usr/local/vpnserver/apply_nat.sh

    cat <<'EOF' > /usr/local/vpnserver/setup_taps.sh
#!/bin/bash
source /etc/os-release
VERSION_MAJOR=$(echo "${VERSION_ID:-}" | cut -d. -f1)

{
flock -s 202
while read -r HUB TAP SUBNET PUB_IP; do
    [[ -z "$TAP" ]] && continue
    tap_ready=0
    for i in {1..20}; do
        if /sbin/ip link show "$TAP" >/dev/null 2>&1; then tap_ready=1; break; fi
        sleep 1
    done
    if [[ "$tap_ready" -eq 0 ]]; then continue; fi

    if command -v nmcli >/dev/null 2>&1; then nmcli dev set "$TAP" managed no 2>/dev/null || true; fi
    if [[ "$ID" =~ (centos|almalinux|rocky|rhel|ol) ]] && [[ "$VERSION_MAJOR" == "7" ]]; then
        echo -e "DEVICE=$TAP\nBOOTPROTO=none\nONBOOT=yes\nNM_CONTROLLED=no" > "/etc/sysconfig/network-scripts/ifcfg-$TAP"
    fi
    /sbin/ip link set dev "$TAP" up
    sleep 1
    /sbin/ip addr flush dev "$TAP" 2>/dev/null
    /sbin/ip addr add "${SUBNET}.1/24" dev "$TAP"
    
    if command -v firewall-cmd >/dev/null 2>&1; then
        firewall-cmd --zone=trusted --add-interface="$TAP" --quiet 2>/dev/null || true
    fi
    if command -v ufw >/dev/null 2>&1; then
        ufw allow in on "$TAP" >/dev/null 2>&1 || true
        ufw allow out on "$TAP" >/dev/null 2>&1 || true
        MAIN_IFACE=$(ip -o -4 route show to default | awk '{print $5}' | head -1)
        ufw route allow in on "$TAP" out on "$MAIN_IFACE" >/dev/null 2>&1 || true
        ufw route allow out on "$MAIN_IFACE" in on "$TAP" >/dev/null 2>&1 || true
    fi
done < /etc/vpnserver/ip_map.txt
} 202>"/etc/vpnserver/ip_map.txt.lock"

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
EOF
    chmod +x /usr/local/vpnserver/setup_taps.sh

cat <<EOF > /etc/systemd/system/vpnserver-bridge.service
[Unit]
Description=SoftEther Multi-IP Bridge Helper
Requires=vpnserver.service
After=vpnserver.service

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

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

configure_softether() {
    print_color "yellow" "--> Applying Multi-Protocol Global Settings..."
    SE_ADMIN_PASS=$(openssl rand -hex 10)
    SE_PSK=$(openssl rand -hex 6)
    
    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 400 "$PASS_FILE"; chown root:root "$PASS_FILE"
    
    cd ${SE_DIR}
    local SE_CMD_FILE=$(mktemp /tmp/se_setup.XXXXXX.cmd)
    cat <<EOF > "$SE_CMD_FILE"
ServerPasswordSet ${SE_ADMIN_PASS}
IPsecEnable /L2TP:yes /L2TPRAW:yes /ETHERIP:yes /PSK:${SE_PSK} /DEFAULT:DEFAULT
OpenVpnEnable yes /PORTS:1194
SstpEnable yes
ServerCipherSet ECDHE-RSA-AES256-GCM-SHA384
EOF
    
    timeout 20 ./vpncmd 127.0.0.1 /SERVER /IN:"$SE_CMD_FILE" >/dev/null 2>&1 || true
    rm -f "$SE_CMD_FILE"

    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
    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 "$ADMIN_PASS" || -z "$IPSEC_PSK" ]]; then return 1; fi
}

export_openvpn_config() {
    local USER=$1
    local HUB=$2
    local EXIT_IP=$3
    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" /ADMINHUB:"$HUB" <<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=$(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}_${HUB}_openvpn.ovpn"
            mv "$OVPN_FILE_TMP" "$FINAL_OVPN"
            sed -i "s/^remote .*/remote ${VPN_DOMAIN:-$EXIT_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}
    
    mapfile -t AVAILABLE_IPS < <(get_public_ips)
    if [[ ${#AVAILABLE_IPS[@]} -eq 0 ]]; then print_color "red" "❌ Error: No IP addresses found on $MAIN_IFACE." >&3; return 1; fi

    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 EXISTS=0
        {
            flock -s 202
            while read -r HUB_E TAP_E SUB_E PUB_E; do
                [[ -z "$HUB_E" ]] && continue
                if timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:"$HUB_E" /CMD UserGet "$USERNAME" >/dev/null 2>&1; then
                    EXISTS=1
                    break
                fi
            done < "$IP_MAP_FILE"
        } 202>"${IP_MAP_FILE}.lock"
        
        if [[ "$EXISTS" -eq 1 ]]; then print_color "red" "❌ ERROR: User '${USERNAME}' already exists!" >&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: \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!" >&3; fi
    done

    print_color "cyan" "--- Select Exit IP for [${USERNAME}] ---" >&3
    print_color "cyan" "(Note: On AWS/GCP, select the Private IP bound to $MAIN_IFACE. Cloud IGW will map it to Elastic IP)" >&3
    for i in "${!AVAILABLE_IPS[@]}"; do echo "$((i+1)). ${AVAILABLE_IPS[$i]}" >&3; done
    
    local SELECTED_IP=""
    while true; do
        echo -e -n "\e[33mChoose an IP [1-${#AVAILABLE_IPS[@]}]: \e[0m" >&3
        read -r IP_CHOICE <&3 || IP_CHOICE=""
        if [[ "$IP_CHOICE" =~ ^[0-9]+$ ]] && [[ "$IP_CHOICE" -ge 1 ]] && [[ "$IP_CHOICE" -le ${#AVAILABLE_IPS[@]} ]]; then
            SELECTED_IP=${AVAILABLE_IPS[$((IP_CHOICE-1))]}; break
        else
            print_color "red" "Invalid choice." >&3
        fi
    done

    local TARGET_HUB=""
    if grep -q " $SELECTED_IP$" "$IP_MAP_FILE" 2>/dev/null; then
        TARGET_HUB=$(awk -v ip="$SELECTED_IP" '$4 == ip {print $1}' "$IP_MAP_FILE")
        print_color "yellow" "--> IP $SELECTED_IP is mapped. Reusing Hub: $TARGET_HUB" >&3
    else
        local NEXT_SUB=""
        {
            flock -s 202
            for i in {1..254}; do
                if ! grep -q "^HUB_$i " "$IP_MAP_FILE" 2>/dev/null; then NEXT_SUB=$i; break; fi
            done
        } 202>"${IP_MAP_FILE}.lock"
        
        if [[ -z "$NEXT_SUB" ]]; then
            print_color "red" "❌ ERROR: Subnet pool exhausted (Max 254 hubs)!" >&3
            return 1
        fi
        
        TARGET_HUB="HUB_${NEXT_SUB}"
        local TAP_DEVICE="${NEXT_SUB}"
        local TAP_NAME="tap_${NEXT_SUB}"
        local SUBNET="10.8.${NEXT_SUB}"

        print_color "yellow" "--> Provisioning Virtual Hub ($TARGET_HUB)..." >&3
        timeout 15 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /CMD HubCreate "$TARGET_HUB" /PASSWORD:"$ADMIN_PASS" >/dev/null 2>&1 || { print_color "red" "❌ FATAL: Hub creation failed!" >&3; return 1; }
        timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /CMD BridgeCreate "$TARGET_HUB" /DEVICE:"$TAP_DEVICE" /TAP:yes >/dev/null 2>&1
        
        {
            flock -x 202
            echo "$TARGET_HUB $TAP_NAME $SUBNET $SELECTED_IP" >> "$IP_MAP_FILE"
        } 202>"${IP_MAP_FILE}.lock"

        cat <<EOF > "/etc/dnsmasq.d/softether_${TAP_NAME}.conf"
interface=$TAP_NAME
except-interface=lo
bind-dynamic
dhcp-range=$TAP_NAME,${SUBNET}.10,${SUBNET}.200,12h
dhcp-option=$TAP_NAME,3,${SUBNET}.1
dhcp-option=$TAP_NAME,6,1.1.1.1,1.0.0.1
EOF
        /bin/bash /usr/local/vpnserver/setup_taps.sh >/dev/null 2>&1
    fi

    print_color "cyan" "--> Creating user in $TARGET_HUB..." >&3
    local USER_CMD_FILE=$(mktemp /tmp/user_setup.XXXXXX.cmd)
    cat <<EOF > "$USER_CMD_FILE"
UserCreate $USERNAME /GROUP:none /REALNAME:none /NOTE:none
UserPasswordSet $USERNAME /PASSWORD:$USERPASS
EOF
    timeout 15 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:"$TARGET_HUB" /IN:"$USER_CMD_FILE" >/dev/null 2>&1 || true
    rm -f "$USER_CMD_FILE"
    
    sleep 2
    OVPN_PATH=$(export_openvpn_config "$USERNAME" "$TARGET_HUB" "$SELECTED_IP")

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

    echo -e "\e[31m⚠️ IMPORTANT: For Multi-IP routing, you MUST append @HUB_NAME to the username!\e[0m\n" >&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:-$SELECTED_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}@${TARGET_HUB}\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}@${TARGET_HUB}\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}@${TARGET_HUB}\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:-$SELECTED_IP}\e[0m | Port: \e[32m443\e[0m (or 5555)." >&3
    echo -e "              Select Virtual Hub as '\e[32m${TARGET_HUB}\e[0m'." >&3
    echo -e "          - User Authentication Setting: " >&3
    echo -e "              Auth Type: Standard Password Authentication" >&3
    echo -e "              Username: \e[32m${USERNAME}\e[0m (Do NOT append @HUB_NAME here)" >&3
    echo -e "              Password: \e[32m${USERPASS}\e[0m\n" >&3
    echo "=================================================================" >&3
}

list_users() {
    load_credentials || return 1
    cd ${SE_DIR}
    
    if [[ ! -s "$IP_MAP_FILE" ]]; then print_color "yellow" "No users/Hubs configured yet." >&3; return 0; fi

    echo "=========================================================================================" >&3
    echo -e "\e[36mUSERNAME\t\tVIRTUAL HUB\t\tEXIT PUBLIC IP\e[0m" >&3
    echo "-----------------------------------------------------------------------------------------" >&3
    
    {
    flock -s 202
    while read -r HUB TAP SUB PUB; do
        [[ -z "$HUB" ]] && continue
        local RAW=$(timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:"$HUB" /CMD UserList 2>/dev/null || true)
        local USERS=""
        if echo "$RAW" | grep -qEi 'Item[[:space:]]*[|│][[:space:]]*Value'; then
            USERS=$(echo "$RAW" | grep -Ei 'User Name[[:space:]]*[|│]' | awk -F '[|│]' '{print $2}' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
        else
            USERS=$(echo "$RAW" | grep '[|│]' | grep -vi 'User Name' | awk -F '[|│]' '{print $1}' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | sed '/^$/d')
        fi
        
        if [[ -n "$USERS" ]]; then
            mapfile -t U_ARRAY <<< "$USERS"
            for U in "${U_ARRAY[@]}"; do
                if [[ -n "$U" && "$U" != "User Name" ]]; then
                    printf "%-24s%-24s%-15s\n" "$U" "$HUB" "$PUB" >&3
                fi
            done
        fi
    done < "$IP_MAP_FILE"
    } 202>"${IP_MAP_FILE}.lock"
    echo "=========================================================================================" >&3
}

revoke_user() {
    load_credentials || return 1
    cd ${SE_DIR}
    
    if [[ ! -s "$IP_MAP_FILE" ]]; then print_color "yellow" "No users to revoke." >&3; return 1; fi

    echo "=========================================================================================" >&3
    echo -e "\e[36mID\tUSERNAME\t\tVIRTUAL HUB\t\tEXIT PUBLIC IP\e[0m" >&3
    echo "-----------------------------------------------------------------------------------------" >&3
    
    local IDX=1
    declare -A USER_MAP
    declare -A HUB_MAP
    declare -A SUB_MAP
    declare -A PUB_MAP

    {
    flock -s 202
    while read -r HUB TAP SUB PUB; do
        [[ -z "$HUB" ]] && continue
        local RAW=$(timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:"$HUB" /CMD UserList 2>/dev/null || true)
        local USERS=""
        if echo "$RAW" | grep -qEi 'Item[[:space:]]*[|│][[:space:]]*Value'; then
            USERS=$(echo "$RAW" | grep -Ei 'User Name[[:space:]]*[|│]' | awk -F '[|│]' '{print $2}' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
        else
            USERS=$(echo "$RAW" | grep '[|│]' | grep -vi 'User Name' | awk -F '[|│]' '{print $1}' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | sed '/^$/d')
        fi
        
        if [[ -n "$USERS" ]]; then
            mapfile -t U_ARRAY <<< "$USERS"
            for U in "${U_ARRAY[@]}"; do
                if [[ -n "$U" && "$U" != "User Name" ]]; then
                    USER_MAP[$IDX]="$U"
                    HUB_MAP[$IDX]="$HUB"
                    SUB_MAP[$IDX]="$SUB"
                    PUB_MAP[$IDX]="$PUB"
                    printf "[%d]\t%-24s%-24s%-15s\n" "$IDX" "$U" "$HUB" "$PUB" >&3
                    IDX=$((IDX+1))
                fi
            done
        fi
    done < "$IP_MAP_FILE"
    } 202>"${IP_MAP_FILE}.lock"
    echo "=========================================================================================" >&3
    
    if [[ $IDX -eq 1 ]]; then print_color "yellow" "No active users found." >&3; return 0; fi

    echo -e -n "\e[33mEnter ID to revoke (or press Enter to cancel): \e[0m" >&3
    read -r SEL_ID <&3 || SEL_ID=""
    if [[ -z "$SEL_ID" ]] || ! [[ "$SEL_ID" =~ ^[0-9]+$ ]] || [[ "$SEL_ID" -lt 1 ]] || [[ "$SEL_ID" -ge $IDX ]]; then return 0; fi

    local TARGET_U="${USER_MAP[$SEL_ID]}"
    local TARGET_H="${HUB_MAP[$SEL_ID]}"
    local TARGET_SUB="${SUB_MAP[$SEL_ID]}"
    local TARGET_PUB="${PUB_MAP[$SEL_ID]}"
    local TARGET_TAP="tap_${TARGET_H#HUB_}"
    
    echo -e -n "\e[31mAre you sure you want to revoke '${TARGET_U}' from ${TARGET_H}? (y/N): \e[0m" >&3
    read -r CONFIRM <&3 || CONFIRM=""
    if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then
        
        print_color "yellow" "--> Disconnecting active sessions for user [${TARGET_U}]..." >&3
        local SESSIONS=$(timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:"$TARGET_H" /CMD SessionList 2>/dev/null | awk -F '[|│]' -v u="$TARGET_U" '{
            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:"$TARGET_H" /CMD SessionDisconnect "$s" >/dev/null 2>&1 || true
            fi
        done

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

        local USER_COUNT=$(timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /ADMINHUB:"$TARGET_H" /CMD UserList 2>/dev/null | grep '[|│]' | grep -vi 'User Name' | awk -F '[|│]' '{print $1}' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | sed '/^$/d' | wc -l || echo 0)
        
        if [[ "$USER_COUNT" -eq 0 ]]; then
            print_color "yellow" "--> Hub $TARGET_H is empty. Executing Garbage Collection..." >&3
            timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /CMD BridgeDelete "$TARGET_H" /DEVICE:"${TARGET_H#HUB_}" >/dev/null 2>&1 || true
            timeout 10 ./vpncmd 127.0.0.1 /SERVER /PASSWORD:"$ADMIN_PASS" /CMD HubDelete "$TARGET_H" >/dev/null 2>&1 || true
            
            {
                flock -x 202
                grep -vw "^$TARGET_H" "$IP_MAP_FILE" > "${IP_MAP_FILE}.tmp" && mv "${IP_MAP_FILE}.tmp" "$IP_MAP_FILE"
            } 202>"${IP_MAP_FILE}.lock"
            
            rm -f "/etc/dnsmasq.d/softether_${TARGET_TAP}.conf"
            ip link set dev "$TARGET_TAP" down 2>/dev/null || true
            ip link delete "$TARGET_TAP" 2>/dev/null || true
            
            if command -v firewall-cmd >/dev/null 2>&1; then
                firewall-cmd --zone=trusted --remove-interface="$TARGET_TAP" --quiet 2>/dev/null || true
            fi
            if command -v ufw >/dev/null 2>&1; then
                grep -vw "\-A POSTROUTING -s ${TARGET_SUB}.0/24 " /etc/ufw/before.rules > /tmp/ufw_clean.tmp && mv /tmp/ufw_clean.tmp /etc/ufw/before.rules
                ufw reload >/dev/null 2>&1 || true
            fi

            if command -v nft >/dev/null 2>&1 && [[ "$ID" != "ubuntu" && "$ID" != "debian" && "$VERSION_MAJOR" != "7" ]]; then
                nft delete rule ip se_nat postrouting ip saddr "${TARGET_SUB}.0/24" oifname "$MAIN_IFACE" snat to "$TARGET_PUB" 2>/dev/null || true
            else
                iptables -t nat -D POSTROUTING -s "${TARGET_SUB}.0/24" -o "$MAIN_IFACE" -j SNAT --to-source "$TARGET_PUB" 2>/dev/null || true
                iptables -D FORWARD -i "$TARGET_TAP" -j ACCEPT 2>/dev/null || true
                iptables -D FORWARD -o "$TARGET_TAP" -j ACCEPT 2>/dev/null || true
            fi
            
            if dnsmasq --test >/dev/null 2>&1; then
                systemctl restart dnsmasq >/dev/null 2>&1 || print_color "red" "⚠️ dnsmasq restart failed during cleanup" >&3
            fi
        fi
    fi
}

remove_softether() {
    print_color "red" "WARNING: COMPLETELY REMOVE SOFTETHER VPN & ALL MULTI-IP ROUTING?" >&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
    
    if command -v nft >/dev/null 2>&1; then nft delete table ip se_nat 2>/dev/null || true; fi
    if [[ -f "$IP_MAP_FILE" ]]; then
        {
        flock -s 202
        while read -r HUB TAP SUBNET PUB_IP; do
            [[ -z "$SUBNET" ]] && continue
            iptables -t nat -D POSTROUTING -s "${SUBNET}.0/24" -o "$MAIN_IFACE" -j SNAT --to-source "$PUB_IP" 2>/dev/null || true
            iptables -D FORWARD -i "$TAP" -j ACCEPT 2>/dev/null || true
            iptables -D FORWARD -o "$TAP" -j ACCEPT 2>/dev/null || true
            if command -v firewall-cmd >/dev/null 2>&1; then
                firewall-cmd --zone=trusted --remove-interface="$TAP" --quiet 2>/dev/null || true
            fi
        done < "$IP_MAP_FILE"
        } 202>"${IP_MAP_FILE}.lock"
    fi

    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/cron.d/softether-ssl-renew
    rm -f /etc/dnsmasq.d/softether_*.conf
    if command -v ufw >/dev/null 2>&1; then
        sed -i "/-j SNAT --to-source/d" /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 || true; fi
    
    systemctl restart dnsmasq 2>/dev/null || true
    systemctl daemon-reload; sysctl --system >/dev/null 2>&1
    
    print_color "green" "✓ SoftEther VPN and all routing 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 Multi-IP Edition is already installed." >&3
    echo "1. Add a new VPN User (Assign IP)" >&3
    echo "2. Revoke a VPN User" >&3
    echo "3. List VPN Users & IP Maps" >&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
    detect_network_os
    pre_flight_install_checks
    
    print_color "yellow" "Starting SoftEther VPN Multi-IP Enterprise Deployment..." >&3
    echo "=================================================================" >&3
    echo -e -n "\e[33mEnter a Domain Name for SSL (Optional, press Enter to skip): \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_base_bridge
    configure_softether
    
    print_color "green" "✓ Multi-IP Core Engine installed and listening successfully!" >&3
    interactive_create_user
fi