#!/usr/bin/env bash

set -Eeuo pipefail
IFS=$'\n\t'
umask 022
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export DEBIAN_FRONTEND=noninteractive

readonly REPO_URL="https://autoscript.interflazz.my.id/tunnel"
readonly REGISTER_URL="https://autoscript.interflazz.my.id/tunnel/register"
readonly INSTALL_ROOT="/var/lib/autoscript-interflazz"
readonly CACHE_DIR="${INSTALL_ROOT}/cache"
readonly SOURCE_DIR="${INSTALL_ROOT}/source"
readonly TEMPLATE_DIR="/etc/autoscript-interflazz/templates"
readonly PAYLOAD_DIR="/usr/local/lib/autoscript-interflazz"
readonly ASSET_REPORT="${INSTALL_ROOT}/installed-assets.tsv"
readonly LOG_FILE="/var/log/autoscript-interflazz-install.log"
readonly SUPPORTED_VERSIONS="20.04 22.04 24.04"

# Seluruh file paket asli. Semua aset diunduh ke mirror SOURCE_DIR, termasuk
# setup.sh, sehingga installer yang sedang berjalan tidak pernah ditimpa.
readonly -a REPO_ASSETS=(
    "LICENSE"
    "README.md"
    "bot/bot.sh"
    "bot/bot.zip"
    "config/config.json"
    "config/configrouting.json"
    "config/dropbear.conf"
    "config/fv-tunnel"
    "config/haproxy.cfg"
    "config/nginx.conf"
    "config/rclone.conf"
    "config/tun.conf"
    "config/xray.conf"
    "files/bbr.sh"
    "files/cf"
    "files/dropbear"
    "files/ftvpn"
    "files/haproxy"
    "files/ipserver"
    "files/issue.net"
    "files/keamanan"
    "files/limittrojan.service"
    "files/limitvless.service"
    "files/limitvmess.service"
    "files/nameserver"
    "files/openvpn"
    "files/password"
    "files/runn.service"
    "files/socks.service"
    "files/sshd"
    "files/tendang"
    "files/trojan"
    "files/trojanx"
    "files/udp-mini"
    "files/udp-mini-1.service"
    "files/udp-mini-2.service"
    "files/udp-mini-3.service"
    "files/vless"
    "files/vlessx"
    "files/vmess"
    "files/vmessx"
    "files/vnstat-2.6.tar.gz"
    "files/ws"
    "files/ws.service"
    "files/xray"
    "images/autoscript.jpg"
    "images/register"
    "images/sshpremium.jpg"
    "info/readme-eng.md"
    "menu/menu.zip"
    "register"
    "setup.sh"
    "update.sh"
)

RED='\033[1;31m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
CYAN='\033[1;36m'
WHITE='\033[1;37m'
NC='\033[0m'

MODE="install"
AUTO_YES=0
DOMAIN="${DOMAIN:-}"
PUBLIC_IP=""
LICENSE_NAME=""
LICENSE_EXPIRES=""
OS_VERSION=""
OS_NAME=""
BACKUP_DIR=""
OPENVPN_CIPHER_DIRECTIVE="data-ciphers"

usage() {
    cat <<'EOF'
Autoscript Interflazz - installer tunneling

Penggunaan:
  bash setup.sh [opsi]

Opsi:
  --domain DOMAIN  Gunakan domain tanpa meminta input
  --yes            Jalankan tanpa pertanyaan konfirmasi
  --check          Periksa OS, lisensi, dan seluruh aset repo tanpa memasang
  --self-test      Validasi template dan sintaks installer tanpa akses root
  --help           Tampilkan bantuan
EOF
}

log() {
    local level="$1"
    shift
    local color="$WHITE"
    case "$level" in
        INFO) color="$CYAN" ;;
        OK) color="$GREEN" ;;
        WARN) color="$YELLOW" ;;
        ERROR) color="$RED" ;;
    esac
    printf '%b[%s]%b %s\n' "$color" "$level" "$NC" "$*"
    if [[ "$MODE" == "install" && -d /var/log ]]; then
        printf '[%s] [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$level" "$*" >>"$LOG_FILE" 2>/dev/null || true
    fi
}

die() {
    log ERROR "$*"
    exit 1
}

on_error() {
    local exit_code=$?
    local line_no="${BASH_LINENO[0]:-?}"
    log ERROR "Instalasi berhenti pada baris ${line_no} dengan kode ${exit_code}."
    log ERROR "Periksa log: ${LOG_FILE}"
    exit "$exit_code"
}

trap on_error ERR

parse_args() {
    while (($#)); do
        case "$1" in
            --domain)
                shift
                (($#)) || die "Nilai --domain belum diisi."
                DOMAIN="$1"
                ;;
            --yes|-y)
                AUTO_YES=1
                ;;
            --check)
                MODE="check"
                ;;
            --self-test)
                MODE="self-test"
                ;;
            --help|-h)
                usage
                exit 0
                ;;
            *)
                die "Opsi tidak dikenal: $1"
                ;;
        esac
        shift
    done
}

print_header() {
    clear 2>/dev/null || true
    printf '%b\n' "${CYAN}┌──────────────────────────────────────────────────────┐${NC}"
    printf '%b\n' "${CYAN}│${NC}${WHITE}          AUTOSCRIPT INTERFLAZZ INSTALLER          ${NC}${CYAN}│${NC}"
    printf '%b\n' "${CYAN}└──────────────────────────────────────────────────────┘${NC}"
}

require_command() {
    command -v "$1" >/dev/null 2>&1 || die "Perintah wajib tidak tersedia: $1"
}

require_root() {
    [[ $EUID -eq 0 ]] || die "Installer wajib dijalankan sebagai root."
}

bootstrap_http_client() {
    if command -v curl >/dev/null 2>&1; then
        return 0
    fi
    log INFO "Memasang curl dan sertifikat CA untuk proses instalasi."
    apt-get update -y
    apt-get install -y --no-install-recommends ca-certificates curl
}

detect_os() {
    [[ -r /etc/os-release ]] || die "Tidak dapat membaca /etc/os-release."
    local os_id=""
    local version_id=""
    local pretty_name=""
    os_id=$(source /etc/os-release; printf '%s' "${ID:-}")
    version_id=$(source /etc/os-release; printf '%s' "${VERSION_ID:-}")
    pretty_name=$(source /etc/os-release; printf '%s' "${PRETTY_NAME:-Ubuntu}")
    [[ "$os_id" == "ubuntu" ]] || die "Sistem tidak didukung: ${pretty_name}. Gunakan Ubuntu 20.04, 22.04, atau 24.04."
    case "$version_id" in
        20.04|22.04|24.04) ;;
        *) die "Versi Ubuntu ${version_id} tidak didukung. Versi yang didukung: ${SUPPORTED_VERSIONS}." ;;
    esac
    case "$(uname -m)" in
        x86_64|amd64) ;;
        *) die "Arsitektur $(uname -m) tidak didukung. Gunakan VPS AMD64/x86_64." ;;
    esac
    [[ -d /run/systemd/system ]] || die "Systemd tidak terdeteksi. Gunakan instalasi Ubuntu standar."
    OS_VERSION="$version_id"
    OS_NAME="$pretty_name"
    log OK "Sistem didukung: ${OS_NAME} ($(uname -m))."
}

fetch_stdout() {
    curl --fail --silent --show-error --location \
        --connect-timeout 15 --max-time 60 \
        --retry 3 --retry-delay 2 --retry-connrefused \
        --proto '=https' --tlsv1.2 "$1"
}

download_file() {
    local relative_path="$1"
    local destination="$2"
    local parent
    local temporary="${destination}.part.$$"
    parent=$(dirname "$destination")
    mkdir -p "$parent"
    curl --fail --silent --show-error --location \
        --connect-timeout 15 --max-time 300 \
        --retry 3 --retry-delay 2 --retry-connrefused \
        --proto '=https' --tlsv1.2 \
        "${REPO_URL}/${relative_path}" -o "$temporary"
    [[ -s "$temporary" ]] || die "Aset kosong: ${relative_path}"
    mv -f "$temporary" "$destination"
}

asset_path() {
    printf '%s/%s' "$SOURCE_DIR" "$1"
}

record_asset() {
    local source="$1"
    local destination="$2"
    local purpose="$3"
    printf '%s\t%s\t%s\n' "$source" "$destination" "$purpose" >>"$ASSET_REPORT"
}

get_public_ip() {
    local endpoint
    local result=""
    for endpoint in \
        "https://ipv4.icanhazip.com" \
        "https://api.ipify.org" \
        "https://ifconfig.me/ip"; do
        result=$(fetch_stdout "$endpoint" 2>/dev/null | tr -d '[:space:]' || true)
        if [[ "$result" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
            PUBLIC_IP="$result"
            break
        fi
    done
    [[ -n "$PUBLIC_IP" ]] || die "IP publik VPS tidak dapat dideteksi."
    log OK "IP publik terdeteksi: ${PUBLIC_IP}."
}

check_license() {
    local register_data
    local matched_line
    local today
    register_data=$(fetch_stdout "$REGISTER_URL") || die "Endpoint IP register tidak dapat diakses."
    register_data=${register_data//$'\r'/}
    matched_line=$(awk -v ip="$PUBLIC_IP" '
        {
            for (i = 1; i <= NF; i++) {
                if ($i == ip) {
                    print
                    exit
                }
            }
        }
    ' <<<"$register_data")
    [[ -n "$matched_line" ]] || die "IP ${PUBLIC_IP} belum terdaftar pada ${REGISTER_URL}."
    LICENSE_NAME=$(awk '{print $2}' <<<"$matched_line")
    LICENSE_EXPIRES=$(awk '{for (i=1;i<=NF;i++) if ($i ~ /^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/) {print $i; exit}}' <<<"$matched_line")
    [[ "$LICENSE_EXPIRES" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || die "Format tanggal lisensi untuk IP ${PUBLIC_IP} tidak valid."
    date -d "$LICENSE_EXPIRES" '+%Y-%m-%d' >/dev/null 2>&1 || die "Tanggal lisensi tidak dapat diproses: ${LICENSE_EXPIRES}."
    today=$(date -u '+%Y-%m-%d')
    [[ "$LICENSE_EXPIRES" > "$today" || "$LICENSE_EXPIRES" == "$today" ]] || die "Lisensi IP ${PUBLIC_IP} telah berakhir pada ${LICENSE_EXPIRES}."
    log OK "Lisensi aktif: ${LICENSE_NAME}, berlaku sampai ${LICENSE_EXPIRES}."
}

validate_repo_assets() {
    local asset
    local http_code
    local failed=0
    log INFO "Memeriksa ${#REPO_ASSETS[@]} aset asli pada repo baru."
    for asset in "${REPO_ASSETS[@]}"; do
        http_code=$(curl --silent --show-error --location --range 0-0 --output /dev/null \
            --write-out '%{http_code}' --connect-timeout 10 --max-time 45 \
            --retry 2 --proto '=https' --tlsv1.2 "${REPO_URL}/${asset}" || true)
        if [[ "$http_code" == "200" || "$http_code" == "206" ]]; then
            log OK "$asset"
        else
            log ERROR "$asset (HTTP ${http_code:-gagal})"
            failed=1
        fi
    done
    ((failed == 0)) || die "Satu atau lebih aset repo belum tersedia."
}

validate_domain() {
    DOMAIN=${DOMAIN,,}
    DOMAIN=${DOMAIN%.}
    [[ ${#DOMAIN} -le 253 ]] || die "Domain terlalu panjang."
    [[ "$DOMAIN" =~ ^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$ ]] || die "Format domain tidak valid: ${DOMAIN}."
}

ask_domain() {
    while [[ -z "$DOMAIN" ]]; do
        read -r -p "Masukkan domain/subdomain yang sudah diarahkan ke VPS: " DOMAIN
    done
    validate_domain
    local resolved=""
    resolved=$(getent ahostsv4 "$DOMAIN" 2>/dev/null | awk '{print $1}' | sort -u | paste -sd ',' - || true)
    if [[ -z "$resolved" ]]; then
        die "Domain ${DOMAIN} belum memiliki DNS A record."
    fi
    if [[ ",${resolved}," != *",${PUBLIC_IP},"* ]]; then
        log WARN "DNS ${DOMAIN} saat ini mengarah ke ${resolved}, bukan langsung ke ${PUBLIC_IP}."
        log WARN "Ini normal bila proxy Cloudflare aktif; pastikan port 80 dan 443 diteruskan ke VPS."
        if ((AUTO_YES == 0)); then
            local answer=""
            read -r -p "Tetap lanjutkan? [y/N]: " answer
            [[ "$answer" =~ ^[Yy]$ ]] || die "Instalasi dibatalkan."
        fi
    fi
    log OK "Domain digunakan: ${DOMAIN}."
}

confirm_installation() {
    ((AUTO_YES == 1)) && return 0
    printf '\nSistem   : %s\n' "$OS_NAME"
    printf 'IP VPS   : %s\n' "$PUBLIC_IP"
    printf 'Domain   : %s\n' "$DOMAIN"
    printf 'Lisensi  : %s (%s)\n\n' "$LICENSE_NAME" "$LICENSE_EXPIRES"
    local answer=""
    read -r -p "Mulai instalasi? [y/N]: " answer
    [[ "$answer" =~ ^[Yy]$ ]] || die "Instalasi dibatalkan."
}

backup_existing_configuration() {
    BACKUP_DIR="${INSTALL_ROOT}/backup-$(date '+%Y%m%d-%H%M%S')"
    mkdir -p "$BACKUP_DIR"
    local target
    for target in \
        /etc/xray \
        /etc/haproxy/haproxy.cfg \
        /etc/nginx/nginx.conf \
        /etc/nginx/conf.d/xray.conf \
        /etc/default/dropbear \
        /etc/openvpn; do
        if [[ -e "$target" ]]; then
            cp -a --parents "$target" "$BACKUP_DIR" 2>/dev/null || true
        fi
    done
    log OK "Cadangan konfigurasi lama dibuat di ${BACKUP_DIR}."
}

install_packages() {
    log INFO "Memperbarui indeks paket Ubuntu."
    printf 'iptables-persistent iptables-persistent/autosave_v4 boolean true\n' | debconf-set-selections
    printf 'iptables-persistent iptables-persistent/autosave_v6 boolean true\n' | debconf-set-selections
    apt-get update -y
    apt-get install -y --no-install-recommends software-properties-common
    add-apt-repository -y universe >/dev/null 2>&1 || true
    apt-get update -y
    apt-get install -y --no-install-recommends \
        at ca-certificates certbot cron curl dnsutils dropbear easy-rsa fail2ban \
        haproxy iproute2 iptables iptables-persistent jq net-tools \
        netfilter-persistent nginx openvpn openssh-server openssl \
        python3 python3-yaml rclone socat unzip uuid-runtime vnstat wget zip
    log OK "Paket sistem selesai dipasang."
}

configure_system() {
    timedatectl set-timezone Asia/Jakarta
    modprobe tcp_bbr >/dev/null 2>&1 || true
    cat >/etc/sysctl.d/99-autoscript-interflazz.conf <<'EOF'
net.ipv4.ip_forward=1
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
fs.file-max=1000000
EOF
    sysctl --system >/dev/null
    systemctl enable --now cron >/dev/null
    systemctl enable --now vnstat >/dev/null || true
    log OK "Timezone, IP forwarding, dan BBR telah dikonfigurasi."
}

configure_ssh() {
    mkdir -p /etc/ssh/sshd_config.d
    cat >/etc/ssh/sshd_config.d/99-autoscript-interflazz.conf <<'EOF'
Port 22
Port 2222
Port 2223
Port 6401
PermitRootLogin yes
PasswordAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes
AllowTcpForwarding yes
X11Forwarding yes
PrintMotd no
ClientAliveInterval 120
ClientAliveCountMax 2
EOF
    sshd -t
    systemctl enable ssh >/dev/null
    systemctl restart ssh
    log OK "OpenSSH aktif pada port 22, 2222, 2223, dan 6401."
}

configure_dropbear() {
    install -m 0644 "$(asset_path config/dropbear.conf)" /etc/default/dropbear
    if [[ -s "$(asset_path files/issue.net)" ]]; then
        install -m 0644 "$(asset_path files/issue.net)" /etc/kyt.txt
    else
        printf 'Autoscript Interflazz\n' >/etc/kyt.txt
    fi
    systemctl enable dropbear >/dev/null
    systemctl restart dropbear
    log OK "Dropbear aktif pada port 109 dan 143."
}

issue_certificate() {
    local cert_dir="/etc/letsencrypt/live/${DOMAIN}"
    systemctl stop haproxy >/dev/null 2>&1 || true
    systemctl stop nginx >/dev/null 2>&1 || true
    if [[ -s "${cert_dir}/fullchain.pem" && -s "${cert_dir}/privkey.pem" ]] && \
       openssl x509 -checkend 172800 -noout -in "${cert_dir}/fullchain.pem" >/dev/null 2>&1; then
        log INFO "Sertifikat Let's Encrypt yang masih berlaku akan digunakan."
    else
        log INFO "Meminta sertifikat Let's Encrypt untuk ${DOMAIN}."
        certbot certonly --standalone --non-interactive --agree-tos \
            --register-unsafely-without-email --preferred-challenges http \
            --keep-until-expiring --domain "$DOMAIN"
    fi
    [[ -s "${cert_dir}/fullchain.pem" && -s "${cert_dir}/privkey.pem" ]] || die "Sertifikat ${DOMAIN} gagal dibuat."
    mkdir -p /etc/xray /etc/haproxy
    install -m 0644 "${cert_dir}/fullchain.pem" /etc/xray/xray.crt
    install -m 0600 "${cert_dir}/privkey.pem" /etc/xray/xray.key
    cat "${cert_dir}/fullchain.pem" "${cert_dir}/privkey.pem" >/etc/haproxy/hap.pem
    chmod 0600 /etc/haproxy/hap.pem
    mkdir -p /etc/letsencrypt/renewal-hooks/deploy
    cat >/etc/letsencrypt/renewal-hooks/deploy/autoscript-interflazz <<EOF
#!/usr/bin/env bash
set -e
install -m 0644 '${cert_dir}/fullchain.pem' /etc/xray/xray.crt
install -m 0600 '${cert_dir}/privkey.pem' /etc/xray/xray.key
cat '${cert_dir}/fullchain.pem' '${cert_dir}/privkey.pem' >/etc/haproxy/hap.pem
chmod 0600 /etc/haproxy/hap.pem
systemctl reload nginx
systemctl reload haproxy
EOF
    chmod 0755 /etc/letsencrypt/renewal-hooks/deploy/autoscript-interflazz
    log OK "Sertifikat TLS terpasang."
}

write_xray_config() {
    local destination="$1"
    cat >"$destination" <<'EOF'
{
  "log": {
    "loglevel": "warning",
    "error": "/var/log/xray/error.log",
    "access": "/var/log/xray/access.log"
  },
  "api": {
    "tag": "api",
    "services": ["HandlerService", "LoggerService", "StatsService"]
  },
  "stats": {},
  "policy": {
    "levels": {
      "0": {
        "handshake": 4,
        "connIdle": 300,
        "uplinkOnly": 2,
        "downlinkOnly": 5,
        "statsUserUplink": true,
        "statsUserDownlink": true
      }
    },
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true,
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 10000,
      "protocol": "dokodemo-door",
      "settings": {"address": "127.0.0.1"},
      "tag": "api"
    },
    {
      "listen": "127.0.0.1",
      "port": 10001,
      "protocol": "vless",
      "settings": {
        "decryption": "none",
        "clients": [
          {"id": "1d1c1d94-6987-4658-a4dc-8821a30fe7e0", "email": "default-vless-ws"}
#vless
        ]
      },
      "streamSettings": {"network": "ws", "wsSettings": {"path": "/vless"}},
      "tag": "vless-ws"
    },
    {
      "listen": "127.0.0.1",
      "port": 10002,
      "protocol": "vmess",
      "settings": {
        "clients": [
          {"id": "1d1c1d94-6987-4658-a4dc-8821a30fe7e0", "alterId": 0, "email": "default-vmess-ws"}
#vmess
        ]
      },
      "streamSettings": {"network": "ws", "wsSettings": {"path": "/vmess"}},
      "tag": "vmess-ws"
    },
    {
      "listen": "127.0.0.1",
      "port": 10003,
      "protocol": "trojan",
      "settings": {
        "clients": [
          {"password": "1d1c1d94-6987-4658-a4dc-8821a30fe7e0", "email": "default-trojan-ws"}
#trojanws
        ]
      },
      "streamSettings": {"network": "ws", "wsSettings": {"path": "/trojan-ws"}},
      "tag": "trojan-ws"
    },
    {
      "listen": "127.0.0.1",
      "port": 10004,
      "protocol": "shadowsocks",
      "settings": {
        "clients": [
          {"method": "aes-128-gcm", "password": "1d1c1d94-6987-4658-a4dc-8821a30fe7e0", "email": "default-ss-ws"}
#ssws
        ],
        "network": "tcp,udp"
      },
      "streamSettings": {"network": "ws", "wsSettings": {"path": "/ss-ws"}},
      "tag": "shadowsocks-ws"
    },
    {
      "listen": "127.0.0.1",
      "port": 10005,
      "protocol": "vless",
      "settings": {
        "decryption": "none",
        "clients": [
          {"id": "1d1c1d94-6987-4658-a4dc-8821a30fe7e0", "email": "default-vless-grpc"}
#vlessgrpc
        ]
      },
      "streamSettings": {"network": "grpc", "grpcSettings": {"serviceName": "vless-grpc"}},
      "tag": "vless-grpc"
    },
    {
      "listen": "127.0.0.1",
      "port": 10006,
      "protocol": "vmess",
      "settings": {
        "clients": [
          {"id": "1d1c1d94-6987-4658-a4dc-8821a30fe7e0", "alterId": 0, "email": "default-vmess-grpc"}
#vmessgrpc
        ]
      },
      "streamSettings": {"network": "grpc", "grpcSettings": {"serviceName": "vmess-grpc"}},
      "tag": "vmess-grpc"
    },
    {
      "listen": "127.0.0.1",
      "port": 10007,
      "protocol": "trojan",
      "settings": {
        "clients": [
          {"password": "1d1c1d94-6987-4658-a4dc-8821a30fe7e0", "email": "default-trojan-grpc"}
#trojangrpc
        ]
      },
      "streamSettings": {"network": "grpc", "grpcSettings": {"serviceName": "trojan-grpc"}},
      "tag": "trojan-grpc"
    },
    {
      "listen": "127.0.0.1",
      "port": 10008,
      "protocol": "shadowsocks",
      "settings": {
        "clients": [
          {"method": "aes-128-gcm", "password": "1d1c1d94-6987-4658-a4dc-8821a30fe7e0", "email": "default-ss-grpc"}
#ssgrpc
        ],
        "network": "tcp,udp"
      },
      "streamSettings": {"network": "grpc", "grpcSettings": {"serviceName": "ss-grpc"}},
      "tag": "shadowsocks-grpc"
    }
  ],
  "outbounds": [
    {"protocol": "freedom", "tag": "direct"},
    {"protocol": "blackhole", "tag": "blocked"}
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {"type": "field", "inboundTag": ["api"], "outboundTag": "api"},
      {"type": "field", "protocol": ["bittorrent"], "outboundTag": "blocked"}
    ]
  }
}
EOF
}

write_nginx_config() {
    local main_destination="$1"
    local xray_destination="$2"
    cat >"$main_destination" <<'EOF'
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;

events {
    worker_connections 4096;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    log_format main '$proxy_protocol_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_user_agent"';
    access_log /var/log/nginx/access.log main;
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 65;
    server_tokens off;
    include /etc/nginx/conf.d/*.conf;
}
EOF
    cat >"$xray_destination" <<EOF
map \$http_upgrade \$connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 1010 proxy_protocol;
    server_name ${DOMAIN};
    set_real_ip_from 127.0.0.1;
    real_ip_header proxy_protocol;
    client_max_body_size 10m;

    location /vless {
        proxy_pass http://127.0.0.1:10001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$proxy_protocol_addr;
    }
    location /vmess {
        proxy_pass http://127.0.0.1:10002;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$proxy_protocol_addr;
    }
    location /trojan-ws {
        proxy_pass http://127.0.0.1:10003;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$proxy_protocol_addr;
    }
    location /ss-ws {
        proxy_pass http://127.0.0.1:10004;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$proxy_protocol_addr;
    }
    location / {
        proxy_pass http://127.0.0.1:10015;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$proxy_protocol_addr;
    }
}

server {
    listen 1012 proxy_protocol;
    server_name ${DOMAIN};
    set_real_ip_from 127.0.0.1;
    real_ip_header proxy_protocol;
    location / {
        proxy_pass http://127.0.0.1:10012;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
    }
}

server {
    listen 8444 ssl http2 proxy_protocol;
    server_name ${DOMAIN};
    ssl_certificate /etc/xray/xray.crt;
    ssl_certificate_key /etc/xray/xray.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    set_real_ip_from 127.0.0.1;
    real_ip_header proxy_protocol;

    location /vless-grpc { grpc_pass grpc://127.0.0.1:10005; }
    location /vmess-grpc { grpc_pass grpc://127.0.0.1:10006; }
    location /trojan-grpc { grpc_pass grpc://127.0.0.1:10007; }
    location /ss-grpc { grpc_pass grpc://127.0.0.1:10008; }
    location /vless {
        proxy_pass http://127.0.0.1:10001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
    }
    location /vmess {
        proxy_pass http://127.0.0.1:10002;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
    }
    location /trojan-ws {
        proxy_pass http://127.0.0.1:10003;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
    }
    location /ss-ws {
        proxy_pass http://127.0.0.1:10004;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
    }
    location / {
        proxy_pass http://127.0.0.1:10015;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
        proxy_set_header Host \$host;
    }
}

server {
    listen 81 ssl;
    server_name ${DOMAIN};
    ssl_certificate /etc/xray/xray.crt;
    ssl_certificate_key /etc/xray/xray.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    root /var/www/html;
    index index.html;
}
EOF
}

write_haproxy_config() {
    local destination="$1"
    cat >"$destination" <<'EOF'
global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log global
    mode tcp
    option dontlognull
    timeout connect 5s
    timeout client 300s
    timeout server 300s

frontend http_multiport
    mode tcp
    bind *:80
    bind *:8080
    bind *:8880
    bind *:2052
    bind *:2082
    bind *:2086
    bind *:2095
    bind *:2097
    tcp-request inspect-delay 5s
    tcp-request content accept if HTTP
    use_backend nginx_ws if HTTP
    default_backend dropbear

frontend tls_multiport
    mode tcp
    bind *:443
    bind *:2053
    bind *:2083
    bind *:2087
    bind *:2096
    bind *:8443
    tcp-request inspect-delay 5s
    tcp-request content accept if { req.ssl_hello_type 1 }
    use_backend nginx_tls if { req.ssl_sni -m found }
    default_backend openvpn_tcp

backend nginx_ws
    mode tcp
    server nginx 127.0.0.1:1010 send-proxy check

backend nginx_tls
    mode tcp
    server nginx 127.0.0.1:8444 send-proxy check

backend dropbear
    mode tcp
    server dropbear 127.0.0.1:109 check

backend openvpn_tcp
    mode tcp
    server openvpn 127.0.0.1:1194 check
EOF
}

configure_xray() {
    mkdir -p /etc/xray /var/log/xray
    install -m 0755 "$(asset_path files/xray)" /usr/local/bin/xray
    ln -sfn /usr/local/bin/xray /usr/bin/xray
    install -m 0644 "$(asset_path config/config.json)" /etc/xray/config.original.json
    install -m 0644 "$(asset_path config/configrouting.json)" /etc/xray/configrouting.original.json
    write_xray_config /etc/xray/config.json
    cp -a /etc/xray/config.json /etc/xray/configrouting.json
    printf '%s\n' "$DOMAIN" >/etc/xray/domain
    printf '%s\n' "$PUBLIC_IP" >/etc/xray/ipvps
    local ip_meta=""
    ip_meta=$(fetch_stdout "https://ipapi.co/${PUBLIC_IP}/json/" 2>/dev/null || true)
    jq -r '.org // "Tidak diketahui"' <<<"${ip_meta:-{}}" >/etc/xray/isp 2>/dev/null || printf 'Tidak diketahui\n' >/etc/xray/isp
    jq -r '.city // "Tidak diketahui"' <<<"${ip_meta:-{}}" >/etc/xray/city 2>/dev/null || printf 'Tidak diketahui\n' >/etc/xray/city
    touch /var/log/xray/access.log /var/log/xray/error.log
    cat >/etc/systemd/system/xray.service <<'EOF'
[Unit]
Description=Xray Service - Autoscript Interflazz
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/xray run -config /etc/xray/config.json
Restart=on-failure
RestartSec=3
LimitNOFILE=1000000

[Install]
WantedBy=multi-user.target
EOF
    /usr/local/bin/xray run -test -config /etc/xray/config.json
    log OK "Konfigurasi Xray VMess, VLESS, Trojan, dan Shadowsocks valid."
}

configure_nginx_haproxy() {
    mkdir -p /etc/nginx/conf.d /etc/haproxy /var/www/html
    install -m 0644 "$(asset_path config/nginx.conf)" "$TEMPLATE_DIR/config/nginx.conf"
    install -m 0644 "$(asset_path config/xray.conf)" "$TEMPLATE_DIR/config/xray.conf"
    install -m 0644 "$(asset_path config/haproxy.cfg)" "$TEMPLATE_DIR/config/haproxy.cfg"
    write_nginx_config /etc/nginx/nginx.conf /etc/nginx/conf.d/xray.conf
    write_haproxy_config /etc/haproxy/haproxy.cfg
    if [[ -e /etc/nginx/sites-enabled/default ]]; then
        unlink /etc/nginx/sites-enabled/default
    fi
    nginx -t
    haproxy -c -f /etc/haproxy/haproxy.cfg
    log OK "Konfigurasi Nginx dan HAProxy valid untuk paket Ubuntu ${OS_VERSION}."
}

setup_openvpn_pki() {
    local easy_rsa_dir="/etc/openvpn/easy-rsa"
    local pki_dir="${easy_rsa_dir}/pki"
    if [[ ! -s "${pki_dir}/ca.crt" || ! -s "${pki_dir}/issued/server.crt" || ! -s "${pki_dir}/private/server.key" ]]; then
        install -d -m 0700 "$easy_rsa_dir"
        cp -a /usr/share/easy-rsa/. "$easy_rsa_dir/"
        (
            cd "$easy_rsa_dir"
            EASYRSA_BATCH=1 ./easyrsa init-pki
            EASYRSA_BATCH=1 EASYRSA_REQ_CN="Autoscript Interflazz CA" ./easyrsa build-ca nopass
            EASYRSA_BATCH=1 ./easyrsa build-server-full server nopass
            EASYRSA_BATCH=1 ./easyrsa gen-dh
            openvpn --genkey --secret "${pki_dir}/ta.key"
        )
    fi
}

write_openvpn_client() {
    local destination="$1"
    local proto="$2"
    local port="$3"
    cat >"$destination" <<EOF
client
dev tun
proto ${proto}
remote ${DOMAIN} ${port}
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
auth-user-pass
auth-nocache
cipher AES-256-CBC
${OPENVPN_CIPHER_DIRECTIVE} AES-256-GCM:AES-128-GCM:AES-256-CBC
auth SHA256
verb 3
<ca>
$(cat /etc/openvpn/easy-rsa/pki/ca.crt)
</ca>
<tls-crypt>
$(cat /etc/openvpn/easy-rsa/pki/ta.key)
</tls-crypt>
EOF
}

configure_openvpn() {
    if openvpn --version | head -n 1 | grep -q 'OpenVPN 2\.4'; then
        OPENVPN_CIPHER_DIRECTIVE="ncp-ciphers"
    else
        OPENVPN_CIPHER_DIRECTIVE="data-ciphers"
    fi
    setup_openvpn_pki
    mkdir -p /etc/openvpn/server /var/www/html
    local pki="/etc/openvpn/easy-rsa/pki"
    install -m 0644 "${pki}/ca.crt" /etc/openvpn/server/ca.crt
    install -m 0644 "${pki}/issued/server.crt" /etc/openvpn/server/server.crt
    install -m 0600 "${pki}/private/server.key" /etc/openvpn/server/server.key
    install -m 0644 "${pki}/dh.pem" /etc/openvpn/server/dh.pem
    install -m 0600 "${pki}/ta.key" /etc/openvpn/server/ta.key
    local pam_plugin=""
    pam_plugin=$(find /usr/lib -type f -name 'openvpn-plugin-auth-pam.so' -print -quit)
    [[ -n "$pam_plugin" ]] || die "Plugin PAM OpenVPN tidak ditemukan."
    cat >/etc/openvpn/server/server-tcp.conf <<EOF
port 1194
proto tcp-server
dev tun
topology subnet
server 10.8.0.0 255.255.255.0
ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/server.crt
key /etc/openvpn/server/server.key
dh /etc/openvpn/server/dh.pem
tls-crypt /etc/openvpn/server/ta.key
plugin ${pam_plugin} login
verify-client-cert none
username-as-common-name
cipher AES-256-CBC
${OPENVPN_CIPHER_DIRECTIVE} AES-256-GCM:AES-128-GCM:AES-256-CBC
auth SHA256
keepalive 10 120
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 8.8.8.8"
persist-key
persist-tun
user nobody
group nogroup
status /var/log/openvpn-status-tcp.log
verb 3
EOF
    cat >/etc/openvpn/server/server-udp.conf <<EOF
port 2200
proto udp
dev tun
topology subnet
server 20.8.0.0 255.255.255.0
ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/server.crt
key /etc/openvpn/server/server.key
dh /etc/openvpn/server/dh.pem
tls-crypt /etc/openvpn/server/ta.key
plugin ${pam_plugin} login
verify-client-cert none
username-as-common-name
cipher AES-256-CBC
${OPENVPN_CIPHER_DIRECTIVE} AES-256-GCM:AES-128-GCM:AES-256-CBC
auth SHA256
keepalive 10 120
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 8.8.8.8"
persist-key
persist-tun
user nobody
group nogroup
status /var/log/openvpn-status-udp.log
verb 3
EOF
    write_openvpn_client /etc/openvpn/tcp.ovpn tcp-client 1194
    write_openvpn_client /etc/openvpn/udp.ovpn udp 2200
    cp -a /etc/openvpn/tcp.ovpn /etc/openvpn/ssl.ovpn
    sed -i 's/remote .* 1194/remote '"$DOMAIN"' 443/' /etc/openvpn/ssl.ovpn
    cp -a /etc/openvpn/ssl.ovpn /etc/openvpn/ws-ssl.ovpn
    install -m 0644 /etc/openvpn/tcp.ovpn /var/www/html/tcp.ovpn
    install -m 0644 /etc/openvpn/udp.ovpn /var/www/html/udp.ovpn
    install -m 0644 /etc/openvpn/ssl.ovpn /var/www/html/ssl.ovpn
    install -m 0644 /etc/openvpn/ws-ssl.ovpn /var/www/html/ws-ssl.ovpn
    (
        cd /var/www/html
        zip -q -j Kyt-Project.zip tcp.ovpn udp.ovpn ssl.ovpn ws-ssl.ovpn
    )
    cat >/var/www/html/index.html <<EOF
<!doctype html>
<html lang="id"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>OpenVPN ${DOMAIN}</title></head>
<body><h1>OpenVPN Config</h1><ul><li><a href="tcp.ovpn">TCP</a></li><li><a href="udp.ovpn">UDP</a></li><li><a href="ssl.ovpn">SSL 443</a></li><li><a href="ws-ssl.ovpn">WS SSL</a></li><li><a href="Kyt-Project.zip">Semua konfigurasi</a></li></ul></body></html>
EOF
    log OK "OpenVPN TCP/UDP dan berkas klien telah dikonfigurasi."
}

install_repo_assets() {
    mkdir -p "$CACHE_DIR" "$SOURCE_DIR"
    local asset
    log INFO "Mengunduh dan menghubungkan ${#REPO_ASSETS[@]} aset asli Autoscript Interflazz."
    for asset in "${REPO_ASSETS[@]}"; do
        download_file "$asset" "$(asset_path "$asset")"
    done
    unzip -tq "$(asset_path menu/menu.zip)" >/dev/null
    unzip -tq "$(asset_path bot/bot.zip)" >/dev/null
    tar -tzf "$(asset_path files/vnstat-2.6.tar.gz)" >/dev/null
    log OK "Seluruh aset repo selesai diunduh dengan struktur direktori aslinya."
}

install_original_assets() {
    install -d -m 0755 \
        "$TEMPLATE_DIR/config" "$TEMPLATE_DIR/files" \
        "$PAYLOAD_DIR/bin" "$PAYLOAD_DIR/scripts" \
        /usr/share/autoscript-interflazz/images \
        /usr/share/doc/autoscript-interflazz \
        /usr/local/src/autoscript-interflazz
    printf 'SUMBER\tTUJUAN\tFUNGSI\n' >"$ASSET_REPORT"

    local asset
    for asset in "${REPO_ASSETS[@]}"; do
        record_asset "$asset" "$(asset_path "$asset")" "mirror sumber asli"
    done

    install -m 0644 "$(asset_path LICENSE)" /usr/share/doc/autoscript-interflazz/LICENSE
    install -m 0644 "$(asset_path README.md)" /usr/share/doc/autoscript-interflazz/README.md
    install -m 0644 "$(asset_path info/readme-eng.md)" /usr/share/doc/autoscript-interflazz/readme-eng.md
    install -m 0644 "$(asset_path register)" /usr/share/doc/autoscript-interflazz/register.snapshot
    install -m 0644 "$(asset_path images/autoscript.jpg)" /usr/share/autoscript-interflazz/images/autoscript.jpg
    install -m 0644 "$(asset_path images/register)" /usr/share/autoscript-interflazz/images/register
    install -m 0644 "$(asset_path images/sshpremium.jpg)" /usr/share/autoscript-interflazz/images/sshpremium.jpg
    record_asset "LICENSE, README.md, info/readme-eng.md, register" "/usr/share/doc/autoscript-interflazz" "dokumentasi"
    record_asset "images/*" "/usr/share/autoscript-interflazz/images" "gambar asli"

    for asset in config/config.json config/configrouting.json config/dropbear.conf \
        config/fv-tunnel config/haproxy.cfg config/nginx.conf config/rclone.conf \
        config/tun.conf config/xray.conf files/password files/sshd files/socks.service; do
        install -m 0644 "$(asset_path "$asset")" "$TEMPLATE_DIR/${asset}"
    done
    record_asset "config/*" "$TEMPLATE_DIR/config" "template konfigurasi asli"
    record_asset "files/password, files/sshd, files/socks.service" "$TEMPLATE_DIR/files" "template kompatibilitas"

    install -m 0755 "$(asset_path config/fv-tunnel)" /usr/local/sbin/fv-tunnel
    install -m 0755 "$(asset_path bot/bot.sh)" /usr/local/sbin/install-bot-autoscript
    install -m 0755 "$(asset_path files/bbr.sh)" /usr/local/sbin/bbr-autoscript
    install -m 0755 "$(asset_path files/cf)" /usr/local/sbin/cf
    install -m 0755 "$(asset_path files/ipserver)" /usr/local/sbin/ipserver-autoscript
    install -m 0755 "$(asset_path files/nameserver)" /usr/local/sbin/nameserver-autoscript
    install -m 0755 "$(asset_path files/openvpn)" /usr/local/sbin/openvpn-autoscript-legacy
    record_asset "config/fv-tunnel, bot/bot.sh, files/*.sh/cf/ipserver/nameserver/openvpn" "/usr/local/sbin" "perintah dan fallback asli"

    install -m 0755 "$(asset_path files/dropbear)" "$PAYLOAD_DIR/bin/dropbear"
    install -m 0755 "$(asset_path files/ftvpn)" "$PAYLOAD_DIR/bin/ftvpn"
    install -m 0755 "$(asset_path files/haproxy)" "$PAYLOAD_DIR/bin/haproxy"
    install -m 0644 "$(asset_path files/vnstat-2.6.tar.gz)" /usr/local/src/autoscript-interflazz/vnstat-2.6.tar.gz
    record_asset "files/dropbear, files/ftvpn, files/haproxy" "$PAYLOAD_DIR/bin" "biner asli tersimpan; runtime memilih paket Ubuntu yang kompatibel"
    record_asset "files/vnstat-2.6.tar.gz" "/usr/local/src/autoscript-interflazz" "sumber asli; runtime memakai paket Ubuntu"

    install -d -m 0700 /root/.config/rclone
    if [[ ! -s /root/.config/rclone/rclone.conf ]]; then
        install -m 0600 "$(asset_path config/rclone.conf)" /root/.config/rclone/rclone.conf
    fi
    record_asset "config/rclone.conf" "/root/.config/rclone/rclone.conf" "konfigurasi backup; konfigurasi lama tidak ditimpa"

    log OK "Seluruh file asli telah dimirror dan dipetakan; laporan: ${ASSET_REPORT}."
}

install_menu_and_helpers() {
    local menu_extract="${CACHE_DIR}/menu-extract"
    local bot_extract="${CACHE_DIR}/bot-extract"
    mkdir -p "$menu_extract" "$bot_extract"
    find "$menu_extract" -mindepth 1 -delete
    find "$bot_extract" -mindepth 1 -delete
    unzip -qo "$(asset_path menu/menu.zip)" -d "$menu_extract"
    unzip -qo "$(asset_path bot/bot.zip)" -d "$bot_extract"
    local script
    while IFS= read -r -d '' script; do
        if [[ "$(od -An -tx1 -N4 "$script" | tr -d '[:space:]')" != "7f454c46" ]]; then
            sed -i 's/\r$//' "$script"
        fi
        install -m 0755 "$script" "/usr/local/sbin/$(basename "$script")"
    done < <(find "$menu_extract/menu" -maxdepth 1 -type f -print0)
    while IFS= read -r -d '' script; do
        sed -i 's/\r$//' "$script"
        install -m 0755 "$script" "/usr/local/sbin/$(basename "$script")"
    done < <(find "$bot_extract/bot" -maxdepth 1 -type f -print0)
    install -m 0755 "$(asset_path update.sh)" /usr/local/sbin/update-autoscript
    install -m 0755 "$(asset_path files/ws)" /usr/bin/ws
    install -m 0644 "$(asset_path config/tun.conf)" /usr/bin/tun.conf
    install -d -m 0755 /usr/local/kyt
    install -m 0755 "$(asset_path files/udp-mini)" /usr/local/kyt/udp-mini
    install -m 0755 "$(asset_path files/vmess)" /etc/xray/limit.vmess
    install -m 0755 "$(asset_path files/vless)" /etc/xray/limit.vless
    install -m 0755 "$(asset_path files/trojan)" /etc/xray/limit.trojan
    install -m 0755 "$(asset_path files/vmessx)" /usr/bin/vmess
    install -m 0755 "$(asset_path files/vlessx)" /usr/bin/vless
    install -m 0755 "$(asset_path files/trojanx)" /usr/bin/trojan
    install -m 0755 "$(asset_path files/tendang)" /usr/bin/tendang
    install -m 0755 "$(asset_path files/keamanan)" /usr/bin/keamanan
    local executable_script
    for executable_script in \
        /etc/xray/limit.vmess /etc/xray/limit.vless /etc/xray/limit.trojan \
        /usr/bin/vmess /usr/bin/vless /usr/bin/trojan /usr/bin/tendang /usr/bin/keamanan; do
        sed -i '1s|^# !/bin/bash$|#!/usr/bin/env bash|' "$executable_script"
    done
    install -m 0644 "$(asset_path files/limitvmess.service)" /etc/systemd/system/limitvmess.service
    install -m 0644 "$(asset_path files/limitvless.service)" /etc/systemd/system/limitvless.service
    install -m 0644 "$(asset_path files/limittrojan.service)" /etc/systemd/system/limittrojan.service
    install -m 0644 "$(asset_path files/udp-mini-1.service)" /etc/systemd/system/udp-mini-1.service
    install -m 0644 "$(asset_path files/udp-mini-2.service)" /etc/systemd/system/udp-mini-2.service
    install -m 0644 "$(asset_path files/udp-mini-3.service)" /etc/systemd/system/udp-mini-3.service
    install -m 0644 "$(asset_path files/ws.service)" /etc/systemd/system/ws.service
    install -m 0644 "$(asset_path files/runn.service)" /etc/systemd/system/runn.service
    record_asset "menu/menu.zip" "/usr/local/sbin" "73 menu asli"
    record_asset "bot/bot.zip" "/usr/local/sbin" "helper bot asli"
    record_asset "files/xray/ws/udp-mini dan helper akun" "/usr/local/bin, /usr/bin, /usr/local/kyt, /etc/xray" "runtime tunnel"
    record_asset "files/*.service" "/etc/systemd/system" "service runtime; socks.service disimpan sebagai template karena memakai Python 2/ws.py yang tidak tersedia di Ubuntu 24"
    log OK "Menu asli dan seluruh helper telah dipasang."
}

create_runtime_directories() {
    mkdir -p \
        /etc/bot \
        /etc/limit/vmess /etc/limit/vless /etc/limit/trojan \
        /etc/kyt/limit/ssh/ip /etc/kyt/limit/vmess/ip /etc/kyt/limit/vless/ip /etc/kyt/limit/trojan/ip \
        /etc/vmess /etc/vless /etc/trojan \
        /etc/shadowsocks/quota-asli /etc/shadowsocks/quota-hasil /etc/shadowsocks/limit \
        /var/log/xray /var/www/html
    touch \
        /etc/bot/.bot.db \
        /etc/ssh/.ssh.db \
        /etc/vmess/.vmess.db \
        /etc/vless/.vless.db \
        /etc/trojan/.trojan.db \
        /etc/shadowsocks/.shadowsocks.db
}

write_monitor_services() {
    local service
    local command
    for service in vmip vlip vtrip vsip; do
        case "$service" in
            vmip) command=/usr/bin/vmess ;;
            vlip) command=/usr/bin/vless ;;
            vtrip) command=/usr/bin/trojan ;;
            vsip) command=/usr/bin/tendang ;;
        esac
        cat >"/etc/systemd/system/${service}.service" <<EOF
[Unit]
Description=Autoscript Interflazz account monitor (${service})
After=network-online.target xray.service
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/bash ${command}
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF
    done
    cat >/etc/systemd/system/keamanan.service <<'EOF'
[Unit]
Description=Autoscript Interflazz security monitor
After=network-online.target xray.service
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/bash /usr/bin/keamanan
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF
    cat >/etc/systemd/system/rc-local.service <<'EOF'
[Unit]
Description=/etc/rc.local Compatibility
ConditionFileIsExecutable=/etc/rc.local
After=network.target

[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF
    cat >/etc/rc.local <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
    chmod 0755 /etc/rc.local
}

configure_firewall() {
    local interface
    interface=$(ip -4 route show default | awk 'NR==1 {print $5}')
    [[ -n "$interface" ]] || die "Interface jaringan utama tidak ditemukan."
    iptables -C FORWARD -s 10.8.0.0/24 -j ACCEPT 2>/dev/null || iptables -A FORWARD -s 10.8.0.0/24 -j ACCEPT
    iptables -C FORWARD -s 20.8.0.0/24 -j ACCEPT 2>/dev/null || iptables -A FORWARD -s 20.8.0.0/24 -j ACCEPT
    iptables -t nat -C POSTROUTING -s 10.8.0.0/24 -o "$interface" -j MASQUERADE 2>/dev/null || \
        iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o "$interface" -j MASQUERADE
    iptables -t nat -C POSTROUTING -s 20.8.0.0/24 -o "$interface" -j MASQUERADE 2>/dev/null || \
        iptables -t nat -A POSTROUTING -s 20.8.0.0/24 -o "$interface" -j MASQUERADE
    netfilter-persistent save >/dev/null
    if command -v ufw >/dev/null 2>&1 && ufw status | grep -q '^Status: active'; then
        local port
        for port in 22 80 81 109 143 443 1194 2052 2053 2082 2083 2086 2087 2095 2096 2097 2200 2222 2223 6401 8080 8443 8880; do
            ufw allow "$port" >/dev/null
        done
        ufw allow 7100:7300/udp >/dev/null
    fi
    log OK "NAT dan aturan firewall layanan telah disiapkan."
}

configure_cron_and_profile() {
    cat >/etc/cron.d/autoscript-interflazz <<'EOF'
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
15 0 * * * root /usr/bin/bash /usr/local/sbin/xp >/dev/null 2>&1
30 2 * * * root /usr/bin/certbot renew --quiet >/dev/null 2>&1
EOF
    chmod 0644 /etc/cron.d/autoscript-interflazz
    cat >/etc/profile.d/autoscript-interflazz.sh <<'EOF'
if [[ $EUID -eq 0 && $- == *i* && -x /usr/local/sbin/welcome ]]; then
    /usr/local/sbin/welcome
fi
EOF
    chmod 0644 /etc/profile.d/autoscript-interflazz.sh
}

enable_services() {
    systemctl daemon-reload
    local service
    local services=(
        ssh dropbear openvpn-server@server-tcp openvpn-server@server-udp
        xray ws nginx haproxy netfilter-persistent
        udp-mini-1 udp-mini-2 udp-mini-3 runn rc-local
        limitvmess limitvless limittrojan vmip vlip vtrip vsip keamanan
    )
    for service in "${services[@]}"; do
        systemctl enable "$service" >/dev/null
    done
    systemctl restart openvpn-server@server-tcp
    systemctl restart openvpn-server@server-udp
    systemctl restart xray
    systemctl restart ws
    systemctl restart nginx
    systemctl restart haproxy
    systemctl restart udp-mini-1 udp-mini-2 udp-mini-3
    systemctl restart limitvmess limitvless limittrojan
    systemctl restart vmip vlip vtrip vsip keamanan
    systemctl restart rc-local
}

verify_services() {
    local services=(ssh dropbear openvpn-server@server-tcp openvpn-server@server-udp xray ws nginx haproxy udp-mini-1 udp-mini-2 udp-mini-3)
    local service
    local failed=0
    for service in "${services[@]}"; do
        if systemctl is-active --quiet "$service"; then
            log OK "Service ${service}: aktif"
        else
            log ERROR "Service ${service}: gagal aktif"
            systemctl status "$service" --no-pager -l >>"$LOG_FILE" 2>&1 || true
            failed=1
        fi
    done
    ((failed == 0)) || die "Ada service utama yang gagal aktif. Detail tersedia di ${LOG_FILE}."
    ss -lntup >>"$LOG_FILE" 2>&1 || true
}

verify_asset_connections() {
    local asset
    local failed=0
    for asset in "${REPO_ASSETS[@]}"; do
        if [[ ! -s "$(asset_path "$asset")" ]]; then
            log ERROR "Aset tidak terhubung: ${asset}"
            failed=1
        fi
    done
    local destination
    for destination in \
        /usr/local/bin/xray /usr/bin/ws /usr/bin/tun.conf \
        /etc/xray/config.json /etc/xray/limit.vmess /etc/xray/limit.vless /etc/xray/limit.trojan \
        /usr/bin/vmess /usr/bin/vless /usr/bin/trojan /usr/bin/tendang /usr/bin/keamanan \
        /usr/local/kyt/udp-mini /etc/systemd/system/ws.service \
        /etc/systemd/system/limitvmess.service /etc/systemd/system/limitvless.service \
        /etc/systemd/system/limittrojan.service /usr/local/sbin/menu; do
        if [[ ! -s "$destination" ]]; then
            log ERROR "Tujuan instalasi tidak tersedia: ${destination}"
            failed=1
        fi
    done
    ((failed == 0)) || die "Pemetaan file asli ke komponen runtime belum lengkap."
    log OK "Seluruh aset asli dan komponen SSH/VMess/VLESS/Trojan telah terhubung."
}

run_self_test() {
    require_command bash
    require_command awk
    require_command sed
    local test_dir
    local project_dir
    local asset
    local missing=0
    test_dir=$(mktemp -d)
    project_dir=$(cd "$(dirname "$0")" && pwd)
    for asset in "${REPO_ASSETS[@]}"; do
        if [[ ! -s "${project_dir}/${asset}" ]]; then
            log ERROR "Aset paket hilang atau kosong: ${asset}"
            missing=1
        fi
    done
    ((missing == 0)) || die "Paket tidak lengkap."
    DOMAIN="vpn.example.com"
    write_xray_config "${test_dir}/config.json"
    write_nginx_config "${test_dir}/nginx.conf" "${test_dir}/xray.conf"
    write_haproxy_config "${test_dir}/haproxy.cfg"
    bash -n "$0"
    grep -q '^#vless$' "${test_dir}/config.json"
    grep -q 'listen 8444 ssl http2 proxy_protocol;' "${test_dir}/xray.conf"
    grep -q 'bind \*:443' "${test_dir}/haproxy.cfg"
    if [[ -x "$(dirname "$0")/files/xray" ]]; then
        sed -i "s#/var/log/xray/access.log#${test_dir}/access.log#; s#/var/log/xray/error.log#${test_dir}/error.log#" "${test_dir}/config.json"
        touch "${test_dir}/access.log" "${test_dir}/error.log"
        "$(dirname "$0")/files/xray" run -test -config "${test_dir}/config.json" >/dev/null
    fi
    find "$test_dir" -mindepth 1 -delete
    rmdir "$test_dir"
    log OK "Self-test installer, ${#REPO_ASSETS[@]} aset, dan template berhasil."
}

run_check() {
    require_command curl
    detect_os
    get_public_ip
    check_license
    validate_repo_assets
    log OK "Pemeriksaan selesai. VPS siap menjalankan installer."
}

run_install() {
    require_root
    require_command awk
    print_header
    detect_os
    bootstrap_http_client
    get_public_ip
    check_license
    ask_domain
    confirm_installation
    mkdir -p "$INSTALL_ROOT"
    : >"$LOG_FILE"
    backup_existing_configuration
    install_packages
    configure_system
    install_repo_assets
    install_original_assets
    create_runtime_directories
    configure_ssh
    configure_dropbear
    issue_certificate
    configure_xray
    configure_openvpn
    configure_nginx_haproxy
    install_menu_and_helpers
    write_monitor_services
    verify_asset_connections
    configure_firewall
    configure_cron_and_profile
    enable_services
    verify_services
    print_header
    log OK "Instalasi Autoscript Interflazz selesai tanpa service utama yang gagal."
    printf '\nDomain      : %s\n' "$DOMAIN"
    printf 'IP VPS      : %s\n' "$PUBLIC_IP"
    printf 'Lisensi     : %s (sampai %s)\n' "$LICENSE_NAME" "$LICENSE_EXPIRES"
    printf 'Menu utama  : menu\n'
    printf 'Cek service : run\n'
    printf 'Log install : %s\n\n' "$LOG_FILE"
}

main() {
    parse_args "$@"
    case "$MODE" in
        install) run_install ;;
        check) run_check ;;
        self-test) run_self_test ;;
    esac
}

if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
    main "$@"
fi
