#!/usr/bin/env bash
# onx-webserver-switch-validate — v80.8: Switch sonrası smoke + system subdomain
# validation.
#
# Web server switch (Apache↔Nginx↔OLS↔Caddy) sonrası gerçek HTTP servis
# durumunu sınar. WebServerManager::switchTo() post-verify olarak çağırır
# (non-blocking — warnings collect edilir).
#
# Steps:
#   1. systemctl is-active <driver>      — servis çalışıyor mu
#   2. Port 80/443 binding (ss -ltn)
#   3. HTTP smoke (panel hostname + N customer vhost)
#   4. System subdomain access (webmail/mail/autodiscover/autoconfig)
#   5. Backend service check (php-fpm veya lsphp)
#
# Input (stdin JSON):
#   driver           string   apache | nginx | openlitespeed | litespeed | caddy
#   smoke_hosts      array    Test edilecek hostname list (optional, default=hostname only)
#   smoke_limit      int      Maksimum smoke test sayısı (default 3)
#   timeout_seconds  int      Per-curl timeout (default 5)
#
# Output (stdout JSON):
#   {
#     "ok": bool,
#     "driver": "...",
#     "services_up": {"<driver>": bool, "php-fpm": bool, ...},
#     "ports_bound": {"80": bool, "443": bool},
#     "http_smoke": [{"host":"...","code":NNN,"ok":bool}, ...],
#     "system_subdomains": [{"host":"...","type":"...","code":NNN,"ok":bool}, ...],
#     "warnings": ["...", ...]
#   }
#
# Exit codes: 0=ok 1=invalid-input 2=preflight-fail
#   (validation FAIL'i 0 döner — sonuç warnings'de raporlanır, switch'i bozmaz)
#
# Idempotent + güvenli — hiçbir state değiştirmez (read-only smoke).

set -uo pipefail

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

# ── Read input ───────────────────────────────────────────────────────────────
INPUT=$(cat 2>/dev/null || echo '{}')
if ! printf '%s' "${INPUT}" | jq -e 'type == "object"' >/dev/null 2>&1; then
    onx_die 1 "stdin is not a valid JSON object"
fi

DRIVER=$(onx_json_get "${INPUT}" "driver" "")
SMOKE_LIMIT=$(printf '%s' "${INPUT}" | jq -r '.smoke_limit // 3')
TIMEOUT_SECONDS=$(printf '%s' "${INPUT}" | jq -r '.timeout_seconds // 5')

[[ -z "${DRIVER}" ]] && onx_die 1 "driver required (apache|nginx|openlitespeed|litespeed|caddy)"

# Driver → systemd unit + backend service map
case "${DRIVER}" in
    apache)
        SVC_UNIT="httpd"
        BACKEND_UNIT="php-fpm"
        ;;
    nginx)
        SVC_UNIT="nginx"
        BACKEND_UNIT="php-fpm"
        ;;
    openlitespeed|litespeed)
        SVC_UNIT="lsws"
        BACKEND_UNIT=""  # LSAPI worker'ları OLS içinde, ayrı systemd unit yok
        ;;
    caddy)
        SVC_UNIT="caddy"
        BACKEND_UNIT="php-fpm"
        ;;
    *)
        onx_die 1 "unknown driver: ${DRIVER}"
        ;;
esac

# ── Step 1: systemctl is-active ──────────────────────────────────────────────
WARNINGS_FILE=$(mktemp /tmp/onx-switch-validate-XXXXXX.warn)
trap 'rm -f "${WARNINGS_FILE}"' EXIT
SERVICES_JSON_PARTS=""

push_warning() {
    printf '%s\n' "$1" >> "${WARNINGS_FILE}"
}

if systemctl is-active --quiet "${SVC_UNIT}" 2>/dev/null; then
    SVC_ACTIVE="true"
else
    SVC_ACTIVE="false"
    push_warning "primary service ${SVC_UNIT} is NOT active"
fi
SERVICES_JSON_PARTS="\"${SVC_UNIT}\":${SVC_ACTIVE}"

# Backend service check (php-fpm) — OLS hariç
if [[ -n "${BACKEND_UNIT}" ]]; then
    # php-fpm farklı sürümlerde farklı unit isimleri (php-fpm vs php82-php-fpm)
    BACKEND_ACTIVE="false"
    if systemctl is-active --quiet "${BACKEND_UNIT}" 2>/dev/null; then
        BACKEND_ACTIVE="true"
    elif systemctl is-active --quiet php82-php-fpm 2>/dev/null; then
        BACKEND_ACTIVE="true"
        BACKEND_UNIT="php82-php-fpm"
    elif systemctl is-active --quiet php81-php-fpm 2>/dev/null; then
        BACKEND_ACTIVE="true"
        BACKEND_UNIT="php81-php-fpm"
    else
        # En azından bir php*-fpm running mi?
        if systemctl list-units --type=service --state=active 2>/dev/null | grep -q 'php.*-fpm'; then
            BACKEND_ACTIVE="true"
        fi
    fi
    SERVICES_JSON_PARTS+=",\"${BACKEND_UNIT}\":${BACKEND_ACTIVE}"
    if [[ "${BACKEND_ACTIVE}" != "true" ]]; then
        push_warning "backend ${BACKEND_UNIT} not active (PHP requests will 502)"
    fi
fi

# ── Step 2: Port 80/443 binding ──────────────────────────────────────────────
PORT_80="false"
PORT_443="false"
if command -v ss >/dev/null 2>&1; then
    ss_output=$(ss -ltn 2>/dev/null || echo "")
    if printf '%s' "${ss_output}" | grep -qE ':80\s'; then
        PORT_80="true"
    fi
    if printf '%s' "${ss_output}" | grep -qE ':443\s'; then
        PORT_443="true"
    fi
elif command -v netstat >/dev/null 2>&1; then
    netstat_output=$(netstat -ltn 2>/dev/null || echo "")
    printf '%s' "${netstat_output}" | grep -qE ':80\s' && PORT_80="true"
    printf '%s' "${netstat_output}" | grep -qE ':443\s' && PORT_443="true"
else
    push_warning "ss/netstat unavailable — port binding check skipped"
fi
[[ "${PORT_80}" != "true" ]] && push_warning "port 80 NOT bound (HTTP requests will fail)"
# 443 sadece SSL cert mevcutsa kritik — warning ama not blocking
[[ "${PORT_443}" != "true" ]] && push_warning "port 443 NOT bound (HTTPS requests will fail)"

# ── Step 3: HTTP smoke test ──────────────────────────────────────────────────
SMOKE_HOSTS_JSON_ENTRIES=""
SMOKE_HOSTS=()

# JSON array'den smoke_hosts oku
while IFS= read -r h; do
    [[ -n "$h" ]] && SMOKE_HOSTS+=("$h")
done < <(onx_json_array_items "${INPUT}" "smoke_hosts")

# Boşsa hostname'i default test
if [[ ${#SMOKE_HOSTS[@]} -eq 0 ]]; then
    DEFAULT_HOST=$(hostname -f 2>/dev/null || hostname 2>/dev/null || echo "localhost")
    SMOKE_HOSTS=("${DEFAULT_HOST}")
fi

# Cap at smoke_limit
if [[ ${#SMOKE_HOSTS[@]} -gt ${SMOKE_LIMIT} ]]; then
    SMOKE_HOSTS=("${SMOKE_HOSTS[@]:0:${SMOKE_LIMIT}}")
fi

SMOKE_OK_COUNT=0
SMOKE_FAIL_COUNT=0
for host in "${SMOKE_HOSTS[@]}"; do
    # Loopback resolve etmek için --resolve KULLANMAYIZ — gerçek DNS path
    # (NXDOMAIN olursa 000 döner, switch sonrası DNS hâlâ OK'ydi anlamı).
    # Önce HTTPS dene, fail ise HTTP fallback.
    code=$(curl -kso /dev/null -w "%{http_code}" --max-time "${TIMEOUT_SECONDS}" --connect-timeout 3 "https://${host}/" 2>/dev/null || echo "000")
    proto="https"
    if [[ "${code}" == "000" ]]; then
        code=$(curl -kso /dev/null -w "%{http_code}" --max-time "${TIMEOUT_SECONDS}" --connect-timeout 3 "http://${host}/" 2>/dev/null || echo "000")
        proto="http"
    fi

    # 2xx/3xx/4xx OK (4xx = backend cevap veriyor); 000/5xx FAIL
    is_ok="false"
    if [[ "${code}" =~ ^[234][0-9][0-9]$ ]]; then
        is_ok="true"
        SMOKE_OK_COUNT=$((SMOKE_OK_COUNT + 1))
    else
        SMOKE_FAIL_COUNT=$((SMOKE_FAIL_COUNT + 1))
        push_warning "smoke ${proto}://${host}/ returned ${code}"
    fi

    [[ -n "${SMOKE_HOSTS_JSON_ENTRIES}" ]] && SMOKE_HOSTS_JSON_ENTRIES+=","
    SMOKE_HOSTS_JSON_ENTRIES+="{\"host\":\"${host}\",\"proto\":\"${proto}\",\"code\":${code},\"ok\":${is_ok}}"
done

# ── Step 4: System subdomain access ──────────────────────────────────────────
# SystemSubdomainProvisioner default 6 subdomain. Burada sadece İLK SMOKE_HOSTS[0]
# için kontrol — full sweep network-heavy. Webmail + autodiscover en kritik test
# (PHP handler doğru pool socket'e mi gidiyor?).
SYS_SUBDOMAINS_JSON_ENTRIES=""
SYS_SUBDOMAINS=("webmail" "mail" "autoconfig" "autodiscover")

if [[ ${#SMOKE_HOSTS[@]} -gt 0 ]]; then
    # Apex domain'i hostname'den çıkar (webmail.example.com → example.com,
    # ama panel.x.tr de geçerli — apex bilemeyebiliriz, hostname kullan)
    apex="${SMOKE_HOSTS[0]}"
    # Apex değil de subdomain ise (panel.X.tr, www.X.tr) ilk segmenti at
    if [[ "${apex}" == panel.* ]] || [[ "${apex}" == www.* ]]; then
        apex="${apex#*.}"
    fi

    for sub in "${SYS_SUBDOMAINS[@]}"; do
        sys_host="${sub}.${apex}"
        # DNS resolve check first — yoksa skip (legitimate "no DNS" durumu)
        if command -v host >/dev/null 2>&1; then
            if ! timeout 2 host -t A "${sys_host}" >/dev/null 2>&1; then
                # DNS yok — vhost yazılmış olsa bile test edilmesi anlamsız (NXDOMAIN)
                continue
            fi
        fi
        sys_code=$(curl -kso /dev/null -w "%{http_code}" --max-time "${TIMEOUT_SECONDS}" --connect-timeout 3 "https://${sys_host}/" 2>/dev/null || echo "000")
        sys_proto="https"
        if [[ "${sys_code}" == "000" ]]; then
            sys_code=$(curl -kso /dev/null -w "%{http_code}" --max-time "${TIMEOUT_SECONDS}" --connect-timeout 3 "http://${sys_host}/" 2>/dev/null || echo "000")
            sys_proto="http"
        fi

        sys_ok="false"
        # 2xx/3xx/4xx OK — vhost yüklü ve PHP/redirect cevap veriyor
        if [[ "${sys_code}" =~ ^[234][0-9][0-9]$ ]]; then
            sys_ok="true"
        elif [[ "${sys_code}" != "000" ]]; then
            push_warning "system subdomain ${sys_host} returned ${sys_code}"
        fi

        # Subdomain type guess (provisioner taxonomy paralel)
        sys_type="webmail_proxy"
        case "${sub}" in
            panel) sys_type="panel_redirect" ;;
            webdisk) sys_type="webdisk" ;;
            autoconfig|autodiscover) sys_type="mail_autoconfig" ;;
        esac

        [[ -n "${SYS_SUBDOMAINS_JSON_ENTRIES}" ]] && SYS_SUBDOMAINS_JSON_ENTRIES+=","
        SYS_SUBDOMAINS_JSON_ENTRIES+="{\"host\":\"${sys_host}\",\"type\":\"${sys_type}\",\"proto\":\"${sys_proto}\",\"code\":${sys_code},\"ok\":${sys_ok}}"
    done
fi

# ── Build warnings JSON array ────────────────────────────────────────────────
WARNINGS_JSON="[]"
if [[ -s "${WARNINGS_FILE}" ]]; then
    WARNINGS_JSON=$(jq -R . < "${WARNINGS_FILE}" | jq -s . 2>/dev/null || echo '[]')
fi

# ── Overall ok determination ─────────────────────────────────────────────────
# OK = primary service active + port 80 bound + en az 1 smoke pass
OVERALL_OK="false"
if [[ "${SVC_ACTIVE}" == "true" && "${PORT_80}" == "true" && ${SMOKE_OK_COUNT} -gt 0 ]]; then
    OVERALL_OK="true"
fi

# ── Final JSON ───────────────────────────────────────────────────────────────
# Note: onx_json_out flat key/value only — burada nested JSON için jq -nc kullanıyoruz
jq -nc \
    --arg driver "${DRIVER}" \
    --argjson ok "${OVERALL_OK}" \
    --argjson services "{${SERVICES_JSON_PARTS}}" \
    --argjson port80 "${PORT_80}" \
    --argjson port443 "${PORT_443}" \
    --argjson smoke "[${SMOKE_HOSTS_JSON_ENTRIES}]" \
    --argjson sys "[${SYS_SUBDOMAINS_JSON_ENTRIES}]" \
    --argjson warnings "${WARNINGS_JSON}" \
    --argjson smoke_ok "${SMOKE_OK_COUNT}" \
    --argjson smoke_fail "${SMOKE_FAIL_COUNT}" \
    '{
        ok: $ok,
        driver: $driver,
        services_up: $services,
        ports_bound: {"80": $port80, "443": $port443},
        http_smoke: $smoke,
        smoke_summary: {ok: $smoke_ok, fail: $smoke_fail},
        system_subdomains: $sys,
        warnings: $warnings
    }'
