#!/usr/bin/env bash
# =============================================================================
# onx-ols-listener-prepare — Inject 80/443 listeners into OpenLiteSpeed
#                            httpd_config.conf so OLS actually serves port 80/443
#                            (default install only listens on 8088).
#
# v77: WebServerManager::switchTo() → OpenLiteSpeedAdapter::prepareForActivation()
# Called BEFORE lsws systemctl start, so when the service comes up it already
# binds 80/443.
#
# Strategy:
#   - Read /usr/local/lsws/conf/httpd_config.conf
#   - If `listener panelHTTP` / `listener panelHTTPS` blocks missing → append
#   - HTTPS listener uses PANEL cert (panel.onoxsoft.com.tr fullchain + key);
#     customer SNI cert mapping deferred to v78 (sniMap directive)
#   - Atomic write to temp + lswsctrl restart (configtest gate) + rollback on fail
#   - Idempotent — re-runs are no-ops if listeners exist
#
# Input (stdin JSON):
#   {}                              (no args — discovers panel cert paths itself)
#
# Output (stdout JSON):
#   { "ok": true, "listeners_added": N, "backup": "...",
#     "message": "...", "https_skipped": bool }
#
# Exit codes (per _lib/common.sh convention):
#   0  — success
#   1  — invalid input
#   2  — preflight fail (lsws not installed)
#   3  — execution fail (write/restart)
#   4  — execution fail + rollback succeeded (configtest failed)
#
# Sudoers:
#   apache ALL=(root) NOPASSWD: /usr/local/onoxsoft/bin/onx-ols-listener-prepare
#
# Deployed to: /usr/local/onoxsoft/bin/onx-ols-listener-prepare
# =============================================================================

set -euo pipefail

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=_lib/common.sh
source "${SCRIPT_DIR}/_lib/common.sh"

# ── Constants ────────────────────────────────────────────────────────────────
LSWS_BASE="/usr/local/lsws"
HTTPD_CONF="${LSWS_BASE}/conf/httpd_config.conf"
LSWS_CTL="${LSWS_BASE}/bin/lswsctrl"
PANEL_CERT_DIR="/etc/onoxsoft/ssl"
# Panel hostname — onoxsoft-panel cert default name; install.sh issues here.
# We discover by glob since user might host panel under a custom hostname.
HTTPS_LISTENER_NAME="panelHTTPS"
HTTP_LISTENER_NAME="panelHTTP"

# ── Preflight ────────────────────────────────────────────────────────────────
[[ -d "${LSWS_BASE}" ]]   || onx_die 2 "OpenLiteSpeed not installed: ${LSWS_BASE} not found"
[[ -f "${HTTPD_CONF}" ]]  || onx_die 2 "httpd_config.conf not found: ${HTTPD_CONF}"
[[ -x "${LSWS_CTL}" ]]    || onx_die 2 "lswsctrl not executable: ${LSWS_CTL}"

# ── Read input (validation only — no args used currently) ────────────────────
onx_json_input

# ── Discover panel cert for HTTPS listener ───────────────────────────────────
# Sıra: onx-cert path > acme.sh ecc > acme.sh standart > letsencrypt
PANEL_CERT=""
PANEL_KEY=""
HTTPS_SKIPPED="false"

discover_panel_cert() {
    local panel_host
    panel_host=$(hostname -f 2>/dev/null || hostname || echo "panel")

    # 1) Standardize: /etc/onoxsoft/ssl/<hostname>.fullchain + .key
    if [[ -f "${PANEL_CERT_DIR}/${panel_host}.fullchain" ]] && \
       [[ -f "${PANEL_CERT_DIR}/${panel_host}.key" ]]; then
        PANEL_CERT="${PANEL_CERT_DIR}/${panel_host}.fullchain"
        PANEL_KEY="${PANEL_CERT_DIR}/${panel_host}.key"
        return 0
    fi

    # 2) Glob /etc/onoxsoft/ssl/*.fullchain (panel.onoxsoft.com.tr vs etc)
    local cand
    for cand in "${PANEL_CERT_DIR}"/*.fullchain; do
        [[ -f "$cand" ]] || continue
        local key="${cand%.fullchain}.key"
        if [[ -f "$key" ]]; then
            PANEL_CERT="$cand"
            PANEL_KEY="$key"
            return 0
        fi
    done

    # 3) acme.sh ECC
    for cand in /etc/acme.sh/*_ecc/*.cer; do
        [[ -f "$cand" ]] || continue
        local key="${cand%.cer}.key"
        if [[ -f "$key" ]]; then
            PANEL_CERT="$cand"
            PANEL_KEY="$key"
            return 0
        fi
    done

    # 4) Let's Encrypt
    for cand in /etc/letsencrypt/live/*/fullchain.pem; do
        [[ -f "$cand" ]] || continue
        local dir
        dir=$(dirname "$cand")
        if [[ -f "${dir}/privkey.pem" ]]; then
            PANEL_CERT="$cand"
            PANEL_KEY="${dir}/privkey.pem"
            return 0
        fi
    done

    return 1
}

if ! discover_panel_cert; then
    HTTPS_SKIPPED="true"
    onx_log "panel cert not found — HTTPS listener will be SKIPPED (HTTP listener still injected)"
fi

# ── Detect what already exists ───────────────────────────────────────────────
HTTP_EXISTS="false"
HTTPS_EXISTS="false"
LISTENERS_ADDED=0

# Match `listener panelHTTP {` (whitespace tolerant)
if grep -qE "^[[:space:]]*listener[[:space:]]+${HTTP_LISTENER_NAME}[[:space:]]*\{" "${HTTPD_CONF}"; then
    HTTP_EXISTS="true"
fi
if grep -qE "^[[:space:]]*listener[[:space:]]+${HTTPS_LISTENER_NAME}[[:space:]]*\{" "${HTTPD_CONF}"; then
    HTTPS_EXISTS="true"
fi

# Also check: is there ANY listener on *:80 / *:443? (other names like "Default")
# If so, log a warning but still inject our panel-managed ones IF they don't exist by name.
# (OLS allows multiple listeners on different addresses, but two on the same *:80
# would conflict. We rely on the named-listener idempotency.)
PORT_80_BOUND="false"
PORT_443_BOUND="false"
if grep -qE "address[[:space:]]+\*:80[[:space:]]*$|address[[:space:]]+\*:80[[:space:]]*[^0-9]" "${HTTPD_CONF}"; then
    PORT_80_BOUND="true"
fi
if grep -qE "address[[:space:]]+\*:443[[:space:]]*$|address[[:space:]]+\*:443[[:space:]]*[^0-9]" "${HTTPD_CONF}"; then
    PORT_443_BOUND="true"
fi

# If port 80/443 already bound by another listener AND our named listener missing,
# warn but DON'T inject (would cause port collision on lsws restart).
if [[ "${HTTP_EXISTS}" == "false" ]] && [[ "${PORT_80_BOUND}" == "true" ]]; then
    onx_log "warning: port *:80 already bound by other listener; skipping panelHTTP injection"
    HTTP_EXISTS="true"  # treat as exists → skip
fi
if [[ "${HTTPS_EXISTS}" == "false" ]] && [[ "${PORT_443_BOUND}" == "true" ]]; then
    onx_log "warning: port *:443 already bound by other listener; skipping panelHTTPS injection"
    HTTPS_EXISTS="true"  # treat as exists → skip
fi

# Early exit if both already present — idempotent path
if [[ "${HTTP_EXISTS}" == "true" ]] && [[ "${HTTPS_EXISTS}" == "true" ]]; then
    onx_json_out \
        "ok"               "true" \
        "listeners_added"  "0" \
        "backup"           "" \
        "https_skipped"    "${HTTPS_SKIPPED}" \
        "message"          "listeners already present (idempotent skip)"
    exit 0
fi

# ── Backup ───────────────────────────────────────────────────────────────────
BACKUP="${HTTPD_CONF}.bak-$(date +%s)"
cp -p "${HTTPD_CONF}" "${BACKUP}" || onx_die 3 "backup_failed"
onx_log "Backup created: ${BACKUP}"
onx_rollback_register "mv -f '${BACKUP}' '${HTTPD_CONF}' && '${LSWS_CTL}' restart >/dev/null 2>&1 || true"
trap 'onx_rollback_run' ERR

# ── Build listener blocks ────────────────────────────────────────────────────
HTTP_BLOCK=""
HTTPS_BLOCK=""

if [[ "${HTTP_EXISTS}" == "false" ]]; then
    # Use a HEREDOC (NOT awk template — that's banned per project rules).
    HTTP_BLOCK=$(cat <<HTTPEOF

listener ${HTTP_LISTENER_NAME} {
  address                 *:80
  secure                  0
  map                     Example *
}
HTTPEOF
)
    LISTENERS_ADDED=$((LISTENERS_ADDED + 1))
fi

if [[ "${HTTPS_EXISTS}" == "false" ]] && [[ "${HTTPS_SKIPPED}" == "false" ]]; then
    HTTPS_BLOCK=$(cat <<HTTPSEOF

listener ${HTTPS_LISTENER_NAME} {
  address                 *:443
  secure                  1
  keyFile                 ${PANEL_KEY}
  certFile                ${PANEL_CERT}
  certChain               1
  sslProtocol             24
  enableECDHE             1
  renegProtection         1
  sslSessionCache         1
  enableSpdy              15
  enableQuic              1
  map                     Example *
}
HTTPSEOF
)
    LISTENERS_ADDED=$((LISTENERS_ADDED + 1))
fi

# ── Atomic write: temp file → mv ─────────────────────────────────────────────
TMPF=$(mktemp -t onx-ols-httpd-XXXXXX.conf)
chmod 0644 "${TMPF}"
cp -p "${HTTPD_CONF}" "${TMPF}"

# Append new blocks (newline-safe)
{
    [[ -n "${HTTP_BLOCK}" ]] && printf '%s\n' "${HTTP_BLOCK}"
    [[ -n "${HTTPS_BLOCK}" ]] && printf '%s\n' "${HTTPS_BLOCK}"
} >> "${TMPF}"

mv -f "${TMPF}" "${HTTPD_CONF}"
chmod 0644 "${HTTPD_CONF}"
chown lsadm:lsadm "${HTTPD_CONF}" 2>/dev/null || chown root:root "${HTTPD_CONF}" 2>/dev/null || true

onx_log "Wrote httpd_config.conf with ${LISTENERS_ADDED} new listener(s)"

# ── lswsctrl restart (acts as configtest — fails fast on bad config) ─────────
# OLS doesn't have a separate -t; restart is the only validation gate.
# If restart fails, ERR trap → rollback restores backup + restarts old config.
if ! RESTART_OUT=$("${LSWS_CTL}" restart 2>&1); then
    onx_log "lswsctrl restart FAILED: ${RESTART_OUT}"
    false  # trigger ERR trap → rollback
fi

# Disable trap before success output
trap - ERR

# ── Verify listener actually bound (port 80 reachable) ───────────────────────
# Best-effort — if ss not installed, skip.
PORT_80_LISTEN="unknown"
if command -v ss >/dev/null 2>&1; then
    if ss -tln 2>/dev/null | grep -qE ':80[[:space:]]'; then
        PORT_80_LISTEN="true"
    else
        PORT_80_LISTEN="false"
    fi
fi

# ── Success ──────────────────────────────────────────────────────────────────
onx_json_out \
    "ok"               "true" \
    "listeners_added"  "${LISTENERS_ADDED}" \
    "backup"           "${BACKUP}" \
    "https_skipped"    "${HTTPS_SKIPPED}" \
    "port_80_listen"   "${PORT_80_LISTEN}" \
    "message"          "ols listener prepare ok"

exit 0
