#!/usr/bin/env bash
#
# onx-exim-tls-setup — Exim TLS cert + SMTPS port setup
#
# v83.16: Exim default config'inde TLS cert tanımlı değil → self-signed
# 2 saatlik cert + MD5 imza üretir (Outlook reject eder, 0x80042109).
# Plus daemon_smtp_ports'ta 465 yok (SMTPS direct TLS port).
# Plus primary_hostname `${env{HOSTNAME}...}` env expansion FAIL ediyor
# → cert CN'i literal text olarak çıkar (cert mismatch).
#
# Bu script acme.sh'tan domain cert'i bulup Exim'e kopyalar, exim.conf'u
# in-place patch eder (idempotent), restart eder.
#
# Input (stdin JSON):
#   { "domain": "leafport.com.tr" }   ← Outlook'un bağlandığı domain
#   Yoksa: panel hostname'in apex'i kullanılır.
#
# Output:
#   {"ok":true,"domain":"leafport.com.tr","cert":"...","hostname":"panel.onoxsoft.com.tr"}

set -uo pipefail
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
source "${SCRIPT_DIR}/_lib/common.sh"

require_root
require_cmd systemctl
require_cmd sed

EXIM_CONF="/etc/exim/exim.conf"
EXIM_SSL_DIR="/etc/exim/ssl"
ACME_BASE="/usr/local/onoxsoft/acme.sh"

# Input parse (opsiyonel)
INPUT="$(cat 2>/dev/null || echo '{}')"
DOMAIN=""
if echo "$INPUT" | jq -e 'type == "object"' >/dev/null 2>&1; then
    DOMAIN="$(echo "$INPUT" | jq -r '.domain // empty')"
fi

# Domain belirtilmemişse panel hostname'in apex'i
if [[ -z "$DOMAIN" ]]; then
    HOST=$(hostname -f 2>/dev/null || hostname)
    # Subdomain prefix'leri sıyır (mail./panel./webmail./etc.)
    DOMAIN="$HOST"
    for p in mail. panel. webmail. autoconfig. autodiscover. webdisk.; do
        [[ "$DOMAIN" == "${p}"* ]] && DOMAIN="${DOMAIN#$p}" && break
    done
fi

# Acme.sh cert path lookup (ECC öncelik, RSA fallback)
CERT_DIR=""
for suffix in "_ecc" ""; do
    p="${ACME_BASE}/${DOMAIN}${suffix}"
    if [[ -d "$p" ]] && [[ -f "${p}/fullchain.cer" ]] && [[ -f "${p}/${DOMAIN}.key" ]]; then
        CERT_DIR="$p"
        break
    fi
done

# v84.6/v84.8 smart fallback — Panel apex için cert yoksa öncelik:
#   1. DB.email_accounts.domain DISTINCT — gerçek mailbox'ı olan domain
#      (cPanel paritesi: "primary mail domain") — Outlook bu domain'le bağlanır
#      Sebep: mail.<X>.com'a bağlanan client cert.CN = X.com bekler.
#   2. Apex'ten en kısa "boş" olmayan domain — root domain tercih (demo. değil)
#   3. Apex'siz son çare en uzun-ömürlü cert (v84.6 fallback davranışı)
#
# Bu öncelik kullanıcı baz alır: gerçek mail trafiği olan domain Outlook için
# CN match yapar, cert mismatch warning olmaz.
if [[ -z "$CERT_DIR" ]] && [[ -d "$ACME_BASE" ]]; then
    NOW_TS=$(date +%s)

    # v84.8: 1. öncelik — DB'den mailbox domain listesi al (panel'den)
    PHP_BIN_FOR_DB="${PHP_BIN:-/opt/remi/php82/root/usr/bin/php}"
    [[ ! -x "$PHP_BIN_FOR_DB" ]] && PHP_BIN_FOR_DB="/usr/bin/php"
    DB_DOMAINS=""
    if [[ -x "$PHP_BIN_FOR_DB" ]] && [[ -f /opt/onoxsoft/vendor/autoload.php ]]; then
        DB_DOMAINS=$(timeout 3 "$PHP_BIN_FOR_DB" -d display_errors=0 -d log_errors=0 -r '
try {
    require "/opt/onoxsoft/vendor/autoload.php";
    $app = require "/opt/onoxsoft/bootstrap/app.php";
    $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
    if (\Illuminate\Support\Facades\Schema::hasTable("email_accounts")) {
        foreach (\Illuminate\Support\Facades\DB::table("email_accounts")
                ->select("domain")->distinct()->pluck("domain") as $d) {
            echo $d . "\n";
        }
    }
} catch (Throwable $e) {}
' 2>/dev/null)
    fi

    # 1. Öncelik: mailbox domain'leri — cert varsa kullan
    BEST_CERT=""
    BEST_TS=0
    SELECTED_DOMAIN=""
    if [[ -n "$DB_DOMAINS" ]]; then
        while IFS= read -r mailbox_dom; do
            [[ -z "$mailbox_dom" ]] && continue
            for suffix in "_ecc" ""; do
                p="${ACME_BASE}/${mailbox_dom}${suffix}"
                if [[ -d "$p" ]] && [[ -f "${p}/fullchain.cer" ]] && [[ -f "${p}/${mailbox_dom}.key" ]]; then
                    end_date=$(openssl x509 -in "${p}/fullchain.cer" -noout -enddate 2>/dev/null | cut -d= -f2)
                    end_ts=$(date -d "$end_date" +%s 2>/dev/null || echo 0)
                    if [[ "$end_ts" -gt "$NOW_TS" ]] && [[ "$end_ts" -gt "$BEST_TS" ]]; then
                        BEST_TS="$end_ts"
                        BEST_CERT="${p}/fullchain.cer"
                        SELECTED_DOMAIN="$mailbox_dom"
                    fi
                fi
            done
        done <<< "$DB_DOMAINS"
    fi

    # 2. Öncelik: mailbox cert yoksa — acme'de root domain (subdomain DEĞİL) tercih
    # Subdomain heuristic: domain.count('.') >= 3 → muhtemelen subdomain (demo.X.com.tr)
    # Tek tarama: tüm cert'ler için end_ts hesapla, root domain'leri öncelik bonus ver
    if [[ -z "$BEST_CERT" ]]; then
        while IFS= read -r -d '' fullchain; do
            cert_dir=$(dirname "$fullchain")
            cert_base=$(basename "$cert_dir")
            cert_domain="${cert_base%_ecc}"
            [[ ! -f "${cert_dir}/${cert_domain}.key" ]] && continue
            end_date=$(openssl x509 -in "$fullchain" -noout -enddate 2>/dev/null | cut -d= -f2)
            [[ -z "$end_date" ]] && continue
            end_ts=$(date -d "$end_date" +%s 2>/dev/null || echo 0)
            [[ "$end_ts" -le "$NOW_TS" ]] && continue

            # Root domain bonus: dot sayısı <= 2 (com.tr için) veya == 1 (com için)
            dot_count=$(echo -n "$cert_domain" | tr -cd '.' | wc -c)
            # Compound TLD heuristic: com.tr/co.uk için 2 nokta root sayılır
            is_root="false"
            if [[ "$dot_count" -le 1 ]]; then
                is_root="true"
            elif [[ "$cert_domain" == *".com.tr" || "$cert_domain" == *".co.uk" \
                  || "$cert_domain" == *".com.au" || "$cert_domain" == *".org.tr" ]] \
                  && [[ "$dot_count" -eq 2 ]]; then
                is_root="true"
            fi

            # Root domain'i öncele (bonus = en uzun süreyi geçer)
            effective_ts="$end_ts"
            [[ "$is_root" == "true" ]] && effective_ts=$((end_ts + 999999999))  # 30+ yıl bonus

            if [[ "$effective_ts" -gt "$BEST_TS" ]]; then
                BEST_TS="$effective_ts"
                BEST_CERT="$fullchain"
                SELECTED_DOMAIN="$cert_domain"
            fi
        done < <(find "$ACME_BASE" -maxdepth 2 -name "fullchain.cer" -type f -print0 2>/dev/null)
    fi

    if [[ -n "$BEST_CERT" ]]; then
        CERT_DIR=$(dirname "$BEST_CERT")
        DOMAIN="$SELECTED_DOMAIN"
        onx_log "TLS smart fallback: panel apex cert yok; secilen: ${DOMAIN} (DB-mailbox veya root-domain öncelik)"
    fi
fi

if [[ -z "$CERT_DIR" ]]; then
    onx_die 2 "acme.sh cert bulunamadi: ${DOMAIN} (${ACME_BASE}/${DOMAIN}{,_ecc}) + fallback olarak ${ACME_BASE} dizininde gecerli cert yok. Önce 'acme.sh --issue -d ${DOMAIN}' calistirin."
fi

# Exim user check
if ! getent passwd exim >/dev/null 2>&1; then
    onx_die 2 "exim user yok (Exim kurulu degil)"
fi

# Cert dir hazirla
mkdir -p "$EXIM_SSL_DIR"
chown exim:exim "$EXIM_SSL_DIR"
chmod 0750 "$EXIM_SSL_DIR"

# Cert + key kopyala (force overwrite — idempotent)
cp -L "${CERT_DIR}/fullchain.cer" "${EXIM_SSL_DIR}/exim.crt"
cp -L "${CERT_DIR}/${DOMAIN}.key" "${EXIM_SSL_DIR}/exim.key"
chown exim:exim "${EXIM_SSL_DIR}/exim.crt" "${EXIM_SSL_DIR}/exim.key"
chmod 0640 "${EXIM_SSL_DIR}/exim.crt" "${EXIM_SSL_DIR}/exim.key"

# Exim config in-place patch (idempotent — sed replaces existing)
# 1. daemon_smtp_ports: 25:587 → 25:587:465 (SMTPS direct ekle)
sed -i 's|^daemon_smtp_ports[[:space:]]*=.*|daemon_smtp_ports = 25 : 587 : 465|' "$EXIM_CONF"

# 2. primary_hostname: env expansion bug fix (literal hostname)
PANEL_HOST=$(hostname -f 2>/dev/null || hostname)
sed -i "s|^primary_hostname[[:space:]]*=.*|primary_hostname = ${PANEL_HOST}|" "$EXIM_CONF"

# 3. TLS cert + key + tls_on_connect_ports + dns_ipv4_lookup
#    (idempotent: önce sil, sonra ekle)
sed -i '/^tls_certificate[[:space:]]*=/d' "$EXIM_CONF"
sed -i '/^tls_privatekey[[:space:]]*=/d' "$EXIM_CONF"
sed -i '/^tls_on_connect_ports[[:space:]]*=/d' "$EXIM_CONF"
sed -i '/^dns_ipv4_lookup[[:space:]]*=/d' "$EXIM_CONF"
# v83.18: dns_ipv4_lookup = * — Gmail IPv6 PTR/auth reject sorununu önler.
# IPv6 PTR record yoksa (chough vendor IPv6 set etmez), Gmail "5.7.1 IPv6
# guidelines" ile bounce. IPv4 outbound zorlamak Gmail kabul oranını yükseltir.
sed -i "/^primary_hostname[[:space:]]*=/a\\
tls_certificate = ${EXIM_SSL_DIR}/exim.crt\\
tls_privatekey = ${EXIM_SSL_DIR}/exim.key\\
tls_on_connect_ports = 465\\
dns_ipv4_lookup = *" "$EXIM_CONF"

# Config syntax check (exim -bV restart engellemeyen syntax test)
if ! exim -bV >/dev/null 2>&1; then
    onx_die 3 "exim -bV syntax check basarisiz — config rollback gerekli (backup: ${EXIM_CONF}.bak.*)"
fi

# Restart Exim
systemctl restart exim
sleep 2

if ! systemctl is-active --quiet exim; then
    onx_die 3 "Exim restart basarisiz — journalctl -u exim ile detay"
fi

# Port doğrula
PORT_465_OK="false"
PORT_587_OK="false"
( exec 3<>/dev/tcp/127.0.0.1/465 ) 2>/dev/null && PORT_465_OK="true"
( exec 3<>/dev/tcp/127.0.0.1/587 ) 2>/dev/null && PORT_587_OK="true"

# Cert CN doğrula
CERT_CN=$(openssl x509 -in "${EXIM_SSL_DIR}/exim.crt" -noout -subject 2>/dev/null | sed -n 's/.*CN[[:space:]]*=[[:space:]]*//p' | head -1)

onx_json_out \
    ok true \
    domain "$DOMAIN" \
    cert_file "${EXIM_SSL_DIR}/exim.crt" \
    cert_cn "$CERT_CN" \
    hostname "$PANEL_HOST" \
    port_465 "$PORT_465_OK" \
    port_587 "$PORT_587_OK" \
    message "Exim TLS cert + SMTPS port + hostname yapilandirildi (${DOMAIN} acme.sh cert)"
