#!/usr/bin/env bash
# =============================================================================
# PBX Server Optimisation Script
# Run once on each new server installation as root.
# Safe to re-run — all steps are idempotent.
# =============================================================================
set -euo pipefail

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
ok()   { echo -e "${GREEN}[OK]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
info() { echo -e "     $1"; }

if [[ $EUID -ne 0 ]]; then
    echo -e "${RED}[ERROR]${NC} Run as root: sudo $0"
    exit 1
fi

echo ""
echo "============================================"
echo "  PBX Server Optimisation"
echo "============================================"
echo ""

# -----------------------------------------------------------------------------
# 1. SWAP — ensure at least 4GB exists
# -----------------------------------------------------------------------------
echo "--- [1/6] Swap ---"
SWAP_TOTAL=$(free -b | awk '/Swap:/ {print $2}')
SWAP_TARGET=$((4 * 1024 * 1024 * 1024))  # 4GB

if [[ $SWAP_TOTAL -ge $SWAP_TARGET ]]; then
    ok "Swap already >= 4GB ($(free -h | awk '/Swap:/{print $2}'))"
else
    warn "Swap is $(free -h | awk '/Swap:/{print $2}') — adding 4GB swapfile"
    if [[ -f /swapfile ]]; then
        swapoff /swapfile 2>/dev/null || true
        rm -f /swapfile
    fi
    fallocate -l 4G /swapfile
    chmod 600 /swapfile
    mkswap /swapfile
    swapon /swapfile
    # Persist across reboots
    if ! grep -q '/swapfile' /etc/fstab; then
        echo '/swapfile none swap sw 0 0' >> /etc/fstab
    fi
    ok "4GB swapfile created and enabled"
fi

# Set swappiness low — only use swap as last resort on a VoIP server
sysctl -w vm.swappiness=10 >/dev/null
if ! grep -q 'vm.swappiness' /etc/sysctl.d/99-pbx.conf 2>/dev/null; then
    echo 'vm.swappiness=10' >> /etc/sysctl.d/99-pbx.conf
fi
ok "vm.swappiness=10"

# -----------------------------------------------------------------------------
# 2. KERNEL NETWORK TUNING — UDP socket buffers for RTP
# -----------------------------------------------------------------------------
echo ""
echo "--- [2/6] Kernel network tuning ---"

SYSCTL_FILE=/etc/sysctl.d/99-pbx.conf
touch "$SYSCTL_FILE"

apply_sysctl() {
    local key=$1 val=$2
    sysctl -w "${key}=${val}" >/dev/null
    # Update or append in the config file
    if grep -q "^${key}" "$SYSCTL_FILE"; then
        sed -i "s|^${key}.*|${key}=${val}|" "$SYSCTL_FILE"
    else
        echo "${key}=${val}" >> "$SYSCTL_FILE"
    fi
}

apply_sysctl net.core.rmem_max        16777216   # 16MB UDP receive buffer
apply_sysctl net.core.wmem_max        16777216   # 16MB UDP send buffer
apply_sysctl net.core.rmem_default    1048576    # 1MB default
apply_sysctl net.core.wmem_default    1048576
apply_sysctl net.core.netdev_max_backlog 5000    # Queue depth
apply_sysctl net.ipv4.udp_rmem_min    8192
apply_sysctl net.ipv4.udp_wmem_min    8192
apply_sysctl net.ipv4.ip_local_port_range "10000 65000"  # Wider ephemeral range
apply_sysctl net.ipv4.tcp_tw_reuse    1
apply_sysctl net.core.somaxconn       1024

ok "UDP/network kernel parameters applied"

# -----------------------------------------------------------------------------
# 3. ASTERISK SYSTEMD — file descriptor and process limits
# -----------------------------------------------------------------------------
echo ""
echo "--- [3/6] Asterisk systemd limits ---"

OVERRIDE_DIR=/etc/systemd/system/asterisk.service.d
mkdir -p "$OVERRIDE_DIR"
cat > "$OVERRIDE_DIR/limits.conf" <<'EOF'
[Service]
LimitNOFILE=65536
LimitNPROC=8192
LimitCORE=infinity
# Prevent OOM killer from targeting Asterisk
OOMScoreAdjust=-100
EOF

# Ensure Asterisk starts AFTER MariaDB so the CDR/CEL ODBC backends can read
# the table schemas at module load. Without this, a cold reboot can cause
# Asterisk to start first → cdr_adaptive_odbc loads an empty column map →
# silently discards every CDR until manually reloaded.
cat > "$OVERRIDE_DIR/wait-for-mariadb.conf" <<'EOF'
[Unit]
After=mariadb.service mysql.service mysqld.service
Wants=mariadb.service

[Service]
# Defense-in-depth: after Asterisk starts, wait for the DB to be pingable
# then reload the CDR backend so a slow-starting DB never leaves CDR dead.
ExecStartPost=/bin/bash -c 'for i in $(seq 1 30); do mysqladmin ping >/dev/null 2>&1 && break; sleep 2; done; sleep 2; asterisk -rx "module reload cdr_adaptive_odbc.so" >/dev/null 2>&1 || true'
EOF

systemctl daemon-reload
ok "Asterisk systemd limits: LimitNOFILE=65536, OOM protected"

# Ensure the fail2ban whitelist sync waits for MariaDB too (same boot-race fix).
if [ -f /etc/systemd/system/pbx-f2b-whitelist.service ]; then
    F2B_WL_DIR=/etc/systemd/system/pbx-f2b-whitelist.service.d
    mkdir -p "$F2B_WL_DIR"
    cat > "$F2B_WL_DIR/wait-for-mariadb.conf" <<'EOF'
[Unit]
After=mariadb.service mysql.service mysqld.service fail2ban.service
Wants=mariadb.service
EOF
    systemctl daemon-reload
    ok "pbx-f2b-whitelist: boot ordering After=mariadb"
fi

# -----------------------------------------------------------------------------
# 4. CODEC OPUS — install if missing
# -----------------------------------------------------------------------------
echo ""
echo "--- [4/6] Opus codec ---"

if asterisk -rx "module show like codec_opus" 2>/dev/null | grep -q "codec_opus.so"; then
    ok "codec_opus.so already loaded"
else
    warn "codec_opus not loaded — attempting install"
    if apt-get install -y asterisk-modules 2>/dev/null | grep -q "asterisk-modules"; then
        ok "asterisk-modules installed"
    else
        # Try direct download from Digium/Sangoma
        OPUS_URL="https://downloads.asterisk.org/pub/telephony/codec_opus/asterisk-18.0/x86_64/codec_opus-18.0_current-x86_64.tar.gz"
        TMP=$(mktemp -d)
        if curl -fsSL "$OPUS_URL" -o "$TMP/opus.tar.gz" 2>/dev/null; then
            tar -xzf "$TMP/opus.tar.gz" -C "$TMP"
            cp "$TMP"/codec_opus*.so /usr/lib/asterisk/modules/ 2>/dev/null || true
            cp "$TMP"/format_ogg_opus*.so /usr/lib/asterisk/modules/ 2>/dev/null || true
            cp "$TMP"/res_format_attr_opus*.so /usr/lib/asterisk/modules/ 2>/dev/null || true
            ok "Opus codec installed from Asterisk downloads"
        else
            warn "Could not install Opus automatically — download manually from https://downloads.asterisk.org/pub/telephony/codec_opus/"
        fi
        rm -rf "$TMP"
    fi
fi

# -----------------------------------------------------------------------------
# 5. ASTERISK LOGGER — reduce to production verbosity
# -----------------------------------------------------------------------------
echo ""
echo "--- [5/6] Asterisk logger ---"

LOGGER=/etc/asterisk/logger.conf
if grep -q 'debug\|verbose' "$LOGGER" 2>/dev/null; then
    sed -i 's/full => .*/full => notice,warning,error,dtmf/' "$LOGGER"
    ok "Logger: removed debug/verbose from full log"
else
    ok "Logger already at production verbosity"
fi

# -----------------------------------------------------------------------------
# 6. APPLY EXISTING QoS TO ALL ps_endpoints IN DB
# -----------------------------------------------------------------------------
echo ""
echo "--- [6/6] Apply QoS defaults to existing endpoints in database ---"

# Source Laravel .env to get DB credentials
ENV_FILE=/var/www/html/.env
if [[ -f "$ENV_FILE" ]]; then
    DB_HOST=$(grep '^DB_HOST=' "$ENV_FILE" | cut -d= -f2 | tr -d '"' | tr -d "'")
    DB_PORT=$(grep '^DB_PORT=' "$ENV_FILE" | cut -d= -f2 | tr -d '"' | tr -d "'" )
    DB_NAME=$(grep '^DB_DATABASE=' "$ENV_FILE" | cut -d= -f2 | tr -d '"' | tr -d "'")
    DB_USER=$(grep '^DB_USERNAME=' "$ENV_FILE" | cut -d= -f2 | tr -d '"' | tr -d "'")
    DB_PASS=$(grep '^DB_PASSWORD=' "$ENV_FILE" | cut -d= -f2 | tr -d '"' | tr -d "'")

    DB_PORT=${DB_PORT:-3306}

    SQL="UPDATE ps_endpoints SET
        tos_audio = 184,
        tos_video = 136,
        cos_audio = 5,
        cos_video = 4,
        rtp_timeout = COALESCE(NULLIF(rtp_timeout, 0), 60),
        rtp_timeout_hold = COALESCE(NULLIF(rtp_timeout_hold, 0), 300)
        WHERE tos_audio IS NULL OR tos_audio = 0;"

    if mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "$SQL" 2>/dev/null; then
        ROWS=$(mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -se "SELECT ROW_COUNT();" 2>/dev/null || echo "?")
        ok "QoS applied to existing endpoints in database"
    else
        warn "Could not update database — run manually or re-save endpoints in the GUI"
    fi
else
    warn ".env not found at $ENV_FILE — skipping database QoS update"
fi

# -----------------------------------------------------------------------------
# SUMMARY
# -----------------------------------------------------------------------------
echo ""
echo "============================================"
echo "  Optimisation complete"
echo "============================================"
echo ""
echo "  Next steps:"
echo "  1. Reload Asterisk logger:  asterisk -rx 'logger reload'"
echo "  2. Restart Asterisk to apply new file limits:"
echo "     systemctl restart asterisk"
echo "  3. Verify with: asterisk -rx 'core show settings'"
echo "     Check 'Maximum open file handles' is now 65536"
echo ""
