#!/usr/bin/env bash
# =============================================================================
# V-Connect PBX — PHP runtime tuning + log-rotation hardening
# =============================================================================
# Enhances the PHP runtime for the management GUI and stops system logs from
# growing unbounded. Safe to run repeatedly (idempotent).
#
# What it does:
#   1. Enables + tunes OPcache (was shipped DISABLED with a dangling symlink,
#      so every web request recompiled all PHP from source — a big slowdown).
#   2. Raises the web PHP limits from stock 128M/30s (too low for an admin GUI
#      doing CDR/firewall/report pages — it caused 500s on large datasets).
#   3. Hardens /etc/logrotate.d/fail2ban (stock rule was weekly with NO size
#      cap, so a SIP-scan/registration flood grew fail2ban.log to ~500MB).
#
# Applies to every PHP SAPI present (apache2 / fpm / cli) for the running PHP
# version, then restarts the web server so OPcache (a zend_extension) loads.
#
# Usage:
#   sudo bash /var/www/html/scripts/tune-php.sh
#   sudo bash /var/www/html/scripts/tune-php.sh --show   # print plan, change nothing
# =============================================================================

set -uo pipefail

DRY_RUN=0
for a in "$@"; do case "$a" in --show|--dry-run) DRY_RUN=1 ;; esac; done

info()    { echo -e "\033[0;34m▸ $*\033[0m"; }
success() { echo -e "\033[0;32m  ✓ $*\033[0m"; }
warn()    { echo -e "\033[0;33m  ⚠ $*\033[0m"; }
die()     { echo -e "\033[0;31mERROR: $*\033[0m"; exit 1; }

[[ $EUID -ne 0 ]] && die "Must be run as root: sudo bash $0"

# ── Resolve the running PHP version (e.g. 8.2) ───────────────────────────────
PHP_VER="$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;' 2>/dev/null)"
[[ -z "$PHP_VER" ]] && die "Could not determine PHP version (is php on PATH?)"
PHP_ROOT="/etc/php/${PHP_VER}"
[[ -d "$PHP_ROOT" ]] || die "PHP config dir not found: $PHP_ROOT"
info "PHP ${PHP_VER} detected at ${PHP_ROOT}"

# ── Tuning drop-in (managed module, priority 99 so it overrides defaults) ────
TUNING_NAME="vconnect-tuning"
TUNING_FILE="${PHP_ROOT}/mods-available/${TUNING_NAME}.ini"

read -r -d '' TUNING_BODY <<'INI'
; ===========================================================================
; V-Connect PBX PHP tuning — MANAGED by scripts/tune-php.sh. Do not edit.
; priority=99
; (loads after the stock opcache module so these values win)
; ===========================================================================

; --- General runtime (web GUI: CDR / firewall / reports) ---
memory_limit = 512M
max_execution_time = 120
max_input_time = 120
realpath_cache_size = 4096k
realpath_cache_ttl = 600
upload_max_filesize = 64M
post_max_size = 64M

; --- OPcache (compile PHP once, serve from memory) ---
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 1
opcache.revalidate_freq = 2
opcache.save_comments = 1
opcache.jit = off
INI

if [[ $DRY_RUN -eq 1 ]]; then
    info "[dry-run] would enable opcache + write ${TUNING_FILE}:"
    echo "$TUNING_BODY" | sed 's/^/    /'
    info "[dry-run] would harden /etc/logrotate.d/fail2ban and restart the web server."
    exit 0
fi

# ── 1. Repair + enable OPcache ───────────────────────────────────────────────
info "Enabling OPcache"
# The image shipped opcache.ini renamed to opcache.ini.disabled, leaving the
# conf.d symlink dangling. Restore the real module file so phpenmod can manage it.
if [[ ! -f "${PHP_ROOT}/mods-available/opcache.ini" && -f "${PHP_ROOT}/mods-available/opcache.ini.disabled" ]]; then
    cp -a "${PHP_ROOT}/mods-available/opcache.ini.disabled" "${PHP_ROOT}/mods-available/opcache.ini"
    success "Restored mods-available/opcache.ini (was .disabled)"
fi
# Fallback: if no module file exists at all, create a minimal one.
if [[ ! -f "${PHP_ROOT}/mods-available/opcache.ini" ]]; then
    printf '; priority=10\nzend_extension=opcache.so\n' > "${PHP_ROOT}/mods-available/opcache.ini"
    success "Created mods-available/opcache.ini"
fi
phpenmod -v "$PHP_VER" opcache 2>/dev/null && success "opcache module enabled (all SAPIs)" \
    || warn "phpenmod opcache returned non-zero (may already be enabled)"

# ── 2. Write + enable the tuning drop-in ─────────────────────────────────────
info "Writing PHP tuning drop-in"
printf '%s\n' "$TUNING_BODY" > "$TUNING_FILE"
phpenmod -v "$PHP_VER" "$TUNING_NAME" 2>/dev/null && success "Tuning enabled (all SAPIs)" \
    || warn "phpenmod ${TUNING_NAME} returned non-zero"

# Validate the new ini parses cleanly before we restart anything.
if php -v >/dev/null 2>&1; then
    success "PHP config parses cleanly"
else
    warn "PHP reported a config error — review ${TUNING_FILE}"
fi

# ── 3. Harden fail2ban log rotation (daily + size cap) ───────────────────────
info "Hardening fail2ban log rotation"
F2B_ROT="/etc/logrotate.d/fail2ban"
if [[ -f "$F2B_ROT" ]] && ! grep -q "V-Connect" "$F2B_ROT"; then
    cp -a "$F2B_ROT" "${F2B_ROT}.orig-$(date +%Y%m%d)" 2>/dev/null || true
fi
cat > "$F2B_ROT" <<'ROT'
# V-Connect PBX — fail2ban log rotation.
# Stock rule was weekly with no size cap, so a SIP-scan / registration flood
# could grow fail2ban.log to hundreds of MB between rotations (and 500'd the
# Firewall page when the GUI tried to read it). Rotate daily, cap at 50 MB.
/var/log/fail2ban.log {
    daily
    rotate 14
    maxsize 50M
    compress
    delaycompress
    missingok
    notifempty
    create 640 root adm
    postrotate
        fail2ban-client flushlogs 1>/dev/null 2>&1 || true
    endscript
}
ROT
success "Wrote hardened ${F2B_ROT}"

# ── 4. Restart the web server so OPcache (a zend_extension) loads ────────────
# zend_extension changes need a full restart, not a reload.
info "Restarting web server to load OPcache"
RESTARTED=0
if systemctl list-units --type=service 2>/dev/null | grep -q "php${PHP_VER}-fpm"; then
    systemctl restart "php${PHP_VER}-fpm" 2>/dev/null && { success "php${PHP_VER}-fpm restarted"; RESTARTED=1; }
fi
if systemctl is-enabled apache2 >/dev/null 2>&1 || systemctl is-active apache2 >/dev/null 2>&1; then
    apache2ctl configtest 2>&1 | grep -vq "Syntax OK" && warn "apache configtest reported an issue" || true
    systemctl restart apache2 2>/dev/null && { success "apache2 restarted"; RESTARTED=1; }
fi
[[ $RESTARTED -eq 0 ]] && warn "No apache2/php-fpm service restarted — restart your web server manually to load OPcache."

# ── 5. Verify ────────────────────────────────────────────────────────────────
info "Verification"
php -r 'echo "  memory_limit (cli) = ".ini_get("memory_limit")."\n";' 2>/dev/null
if php -m 2>/dev/null | grep -qi opcache; then
    success "OPcache extension is loaded (web SAPI uses opcache.enable=1; CLI stays off by design)"
else
    warn "OPcache not showing in 'php -m' — check ${PHP_ROOT}/mods-available/opcache.ini"
fi

echo ""
success "Done. Tuning is in /etc/php/${PHP_VER}/ and will be inherited by any future clone of this box."
echo "  Verify the web SAPI from a browser: open the GUI → Settings, and check phpinfo/opcache if available."
