#!/usr/bin/env bash
#
# onx-mta-alternatives-set — `alternatives --set mta` ile /usr/sbin/sendmail
# symlink'ini aktif MTA'ya yönlendir.
#
# v92 Agent 2 — feedback_onoxsoft_mta_alternatives_switch.md
#
# Sebep: RHEL/AlmaLinux 9'da `/usr/sbin/sendmail → /etc/alternatives/mta →
# /usr/sbin/sendmail.<driver>` zinciri var. Laravel `Mail::raw()`, cron mail,
# scripted mail'ler hep `/usr/sbin/sendmail`'i çağırır. MTA switch sırasında
# bu zincir update edilmezse, switch sonrası Postfix→Exim'e geçilse bile
# Laravel hâlâ sendmail.postfix'i çağırır, mail kaybolur.
#
# v91 production: Postfix→Exim switch sonrası alternatives'i kimse güncellemedi.
# Manual fix: `alternatives --set mta /usr/sbin/sendmail.exim`.
#
# Input (stdin JSON):
#   {
#     "driver": "exim" | "postfix" | "sendmail"
#   }
#
# Output (stdout JSON):
#   {
#     "ok":              true,
#     "driver":          "exim",
#     "current_link":    "/usr/sbin/sendmail.exim",
#     "smoke_test_ok":   true,
#     "smoke_response":  "220 mail.example.com ESMTP Exim 4.96 ..."
#   }
#
# Exit kodları (_lib/common.sh ile uyumlu):
#   0 = success
#   1 = invalid input
#   2 = preflight fail (alternatives komutu yok — Debian değil)
#   3 = execution fail (smoke test başarısız, rollback denendi)

set -uo pipefail

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

require_root

# ─── Input parse ─────────────────────────────────────────────────────────────
INPUT="$(cat 2>/dev/null || echo '{}')"
onx_require_json "$INPUT"

DRIVER="$(onx_json_get "$INPUT" driver)"
[[ -z "$DRIVER" ]] && onx_die 1 "driver required (exim|postfix|sendmail)"

case "$DRIVER" in
    exim|postfix|sendmail) ;;
    *) onx_die 1 "invalid driver: ${DRIVER} (must be exim|postfix|sendmail)" ;;
esac

# ─── Preflight: alternatives komutu var mı? (Debian/Ubuntu update-alternatives) ─
if ! command -v alternatives >/dev/null 2>&1; then
    if command -v update-alternatives >/dev/null 2>&1; then
        # Debian/Ubuntu yolu — alternatives = update-alternatives
        ALT_BIN="update-alternatives"
    else
        # Hiçbiri yoksa skip (idempotent) — log + ok=true, drift kontrolü
        # daha sonra başka katmandan yapılır.
        onx_log "mta-alternatives-set: 'alternatives' komutu yok (manuel symlink fallback)"
        ALT_BIN=""
    fi
else
    ALT_BIN="alternatives"
fi

# ─── Önceki link'i sakla (rollback için) ─────────────────────────────────────
PREV_LINK=""
if [[ -L /etc/alternatives/mta ]]; then
    PREV_LINK="$(readlink /etc/alternatives/mta 2>/dev/null || true)"
fi

onx_log "mta-alternatives-set start driver=${DRIVER} prev_link=${PREV_LINK:-none} alt_bin=${ALT_BIN:-manual}"

# ─── Driver → target sendmail binary map ─────────────────────────────────────
case "$DRIVER" in
    exim)
        TARGET="/usr/sbin/sendmail.exim"
        # Bazı dağıtımlarda exim sendmail.exim kurmaz; doğrudan exim binary
        # symlink'lemek de çalışır (sendmail uyumlu CLI).
        FALLBACK_BIN="/usr/sbin/exim"
        ;;
    postfix)
        TARGET="/usr/sbin/sendmail.postfix"
        FALLBACK_BIN="/usr/sbin/postfix"
        ;;
    sendmail)
        TARGET="/usr/sbin/sendmail.sendmail"
        FALLBACK_BIN=""  # sendmail.sendmail yoksa sendmail driver fallback yok
        ;;
esac

# ─── Switch işlemi ───────────────────────────────────────────────────────────
SWITCH_OK=0
SWITCH_METHOD=""

if [[ -n "$ALT_BIN" && -f "$TARGET" ]]; then
    # Önce alternatives ile dene
    if "$ALT_BIN" --set mta "$TARGET" >/dev/null 2>&1; then
        SWITCH_OK=1
        SWITCH_METHOD="alternatives"
    fi
fi

if [[ $SWITCH_OK -eq 0 ]]; then
    # Fallback: manuel symlink (alternatives yoksa veya başarısızsa)
    LINK_TARGET="$TARGET"
    if [[ ! -f "$LINK_TARGET" && -n "$FALLBACK_BIN" && -x "$FALLBACK_BIN" ]]; then
        LINK_TARGET="$FALLBACK_BIN"
        onx_log "mta-alternatives-set: ${TARGET} yok, fallback ${FALLBACK_BIN}"
    fi

    if [[ ! -e "$LINK_TARGET" ]]; then
        onx_die 2 "MTA binary bulunamadı: ${TARGET} (${DRIVER} kurulu değil mi?)"
    fi

    mkdir -p /etc/alternatives 2>/dev/null || true
    if ln -sf "$LINK_TARGET" /etc/alternatives/mta 2>/dev/null; then
        SWITCH_OK=1
        SWITCH_METHOD="manual_symlink"
    else
        onx_die 3 "/etc/alternatives/mta symlink yazılamadı"
    fi
fi

# ─── Verify: gerçekten doğru target'a mı işaret ediyor? ──────────────────────
CURRENT_LINK=""
if command -v "$ALT_BIN" >/dev/null 2>&1; then
    CURRENT_LINK="$("$ALT_BIN" --display mta 2>/dev/null | awk '/link currently points to/{print $NF; exit}')"
fi

# Fallback: alternatives display başarısızsa readlink ile
if [[ -z "$CURRENT_LINK" && -L /etc/alternatives/mta ]]; then
    CURRENT_LINK="$(readlink /etc/alternatives/mta 2>/dev/null || true)"
fi

# Driver doğrulama — link içinde driver adı geçmeli (sendmail.exim → "exim")
EXPECTED_MARKER=".${DRIVER}"
case "$DRIVER" in
    exim)    EXPECTED_MARKER_ALT="/exim" ;;
    postfix) EXPECTED_MARKER_ALT="/postfix" ;;
    sendmail) EXPECTED_MARKER_ALT="/sendmail" ;;
esac

if [[ "$CURRENT_LINK" != *"${EXPECTED_MARKER}"* && "$CURRENT_LINK" != *"${EXPECTED_MARKER_ALT}"* ]]; then
    onx_log "mta-alternatives-set WARN: verify mismatch (link=${CURRENT_LINK} expected=*${EXPECTED_MARKER}*)"
fi

# ─── Smoke test: /usr/sbin/sendmail -bs ile EHLO ─────────────────────────────
# `echo "EHLO test" | sendmail -bs` MTA daemon'ı çalıştırmadan local SMTP
# converse yapar; banner satırından (220 ...) driver doğrulanır.
SMOKE_OK=false
SMOKE_RESPONSE=""

if [[ -x /usr/sbin/sendmail ]]; then
    # 3sn timeout — Postfix master fork ya da Exim cold-start için yeterli
    SMOKE_RESPONSE="$(timeout 3 bash -c 'echo "EHLO smoke-test" | /usr/sbin/sendmail -bs 2>&1' | head -1 || true)"

    if [[ "$SMOKE_RESPONSE" == 220* ]]; then
        SMOKE_OK=true
        # Driver tahmin et (banner'dan)
        SMOKE_LOWER="$(printf '%s' "$SMOKE_RESPONSE" | tr '[:upper:]' '[:lower:]')"
        case "$DRIVER" in
            exim)
                [[ "$SMOKE_LOWER" == *"exim"* ]] || onx_log "smoke WARN: banner driver mismatch (exim beklenirken: ${SMOKE_RESPONSE})"
                ;;
            postfix)
                [[ "$SMOKE_LOWER" == *"postfix"* || "$SMOKE_LOWER" == *"esmtp"* ]] || \
                    onx_log "smoke WARN: banner driver mismatch (postfix beklenirken: ${SMOKE_RESPONSE})"
                ;;
        esac
    fi
fi

# ─── Smoke fail → rollback ───────────────────────────────────────────────────
# Banner gelmemişse muhtemelen daemon down (port 25 bind fail veya servis hiç
# başlamadı). Bu durumda eski link'e dön — en azından önceki state restore.
if [[ "$SMOKE_OK" != "true" && -n "$PREV_LINK" && "$PREV_LINK" != "$CURRENT_LINK" ]]; then
    onx_log "mta-alternatives-set: smoke fail → rollback ${CURRENT_LINK} → ${PREV_LINK}"
    if [[ -n "$ALT_BIN" ]] && "$ALT_BIN" --set mta "$PREV_LINK" >/dev/null 2>&1; then
        :
    else
        ln -sf "$PREV_LINK" /etc/alternatives/mta 2>/dev/null || true
    fi
    onx_die 3 "Smoke test başarısız (sendmail -bs banner alınamadı). Önceki link geri yüklendi: ${PREV_LINK}"
fi

onx_log "mta-alternatives-set OK driver=${DRIVER} method=${SWITCH_METHOD} link=${CURRENT_LINK} smoke=${SMOKE_OK}"

# ─── Output ──────────────────────────────────────────────────────────────────
onx_json_out \
    ok             true \
    driver         "$DRIVER" \
    current_link   "${CURRENT_LINK:-unknown}" \
    method         "${SWITCH_METHOD}" \
    smoke_test_ok  "$SMOKE_OK" \
    smoke_response "${SMOKE_RESPONSE:-no_banner}"
