#!/usr/bin/env bash
#
# onx-panel-cert-issue — Panel hostname için Let's Encrypt sertifikası al,
# /etc/onoxsoft/ssl/'e yerleştir, Apache'yi reload et.
#
# Stdin (JSON):
#   {"hostname":"panel.onoxsoft.com.tr","email":"admin@onox.com.tr"}
#
# Stdout (JSON):
#   {"ok":true,"hostname":"panel...","cert_path":"...","expires_at":"2026-08-15T...","issuer":"Let's Encrypt"}
#
# Exit:
#   0 = success
#   1 = invalid input (FQDN değil, vd.)
#   2 = preflight fail (DNS çözmüyor, acme.sh yok, vd.)
#   3 = execution fail (acme.sh issue başarısız)
#
# Notlar:
#   - HTTP-01 challenge kullanılır → Apache port 80'de hostname üzerinden cevap vermeli
#   - Self-signed cert üzerine yazar (panel mevcut yapılandırması korunur)
#   - acme.sh otomatik renew cron'u (install.sh'ta /etc/cron.d/onoxsoft-acme) bu cert'i de yeniler

set -uo pipefail

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=_lib/common.sh
source "${SCRIPT_DIR}/_lib/common.sh" 2>/dev/null || true

# Quote-safe JSON error formatter (sed ile escape — jq dep yok)
die() {
    local msg="$1"
    local code="${2:-3}"
    # backslash + double quote escape
    msg="${msg//\\/\\\\}"
    msg="${msg//\"/\\\"}"
    printf '{"error":"%s","code":%d}\n' "$msg" "$code" >&2
    exit "$code"
}

# ── Input ──────────────────────────────────────────────────────────────────
INPUT=$(cat 2>/dev/null || echo '{}')
HOSTNAME=$(echo "$INPUT" | jq -r '.hostname // ""')
EMAIL=$(echo "$INPUT" | jq -r '.email // ""')

# Hostname validate (FQDN gerekli, IP olamaz)
[[ -z "${HOSTNAME}" ]] && die "hostname gerekli" 1
if [[ "${HOSTNAME}" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
    die "Let's Encrypt IP adresine sertifika veremez. Önce gerçek bir FQDN ayarla (örn: panel.alanadi.com.tr)." 1
fi
[[ "${HOSTNAME}" =~ ^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$ ]] \
    || die "Geçersiz hostname formatı: ${HOSTNAME}" 1

# Default email
[[ -z "${EMAIL}" ]] && EMAIL="admin@${HOSTNAME#*.}"

# ── Preflight ──────────────────────────────────────────────────────────────
# acme.sh konumları (install.sh ile uyumlu — /usr/local/onoxsoft/acme.sh)
# Bazı kurulumlarda /etc/onoxsoft/acme.sh veya /root/.acme.sh olabilir,
# her ihtimali kontrol et.
ACME_BIN=""
for candidate in \
    /usr/local/onoxsoft/acme.sh/acme.sh \
    /etc/onoxsoft/acme.sh/acme.sh \
    /root/.acme.sh/acme.sh \
    /opt/onoxsoft/acme.sh/acme.sh; do
    if [[ -x "${candidate}" ]]; then
        ACME_BIN="${candidate}"
        break
    fi
done

[[ -z "${ACME_BIN}" ]] && die "acme.sh kurulu değil. Aranan yollar: /usr/local/onoxsoft/acme.sh/acme.sh, /etc/onoxsoft/acme.sh/acme.sh, /root/.acme.sh/acme.sh" 2

ACME_HOME="$(dirname "${ACME_BIN}")"
export LE_WORKING_DIR="${ACME_HOME}"

ONOX_HOME="/opt/onoxsoft"
SSL_DIR="/etc/onoxsoft/ssl"
WEBROOT="${ONOX_HOME}/public"

[[ -d "${WEBROOT}" ]]  || die "Panel webroot yok: ${WEBROOT}" 2

# ── Sunucu IPv4 tespiti ─────────────────────────────────────────────────────
SERVER_IPV4=$(ip -4 addr show eth0 2>/dev/null | grep "inet " | awk '{print $2}' | cut -d/ -f1 | head -1)
[[ -z "${SERVER_IPV4}" ]] && SERVER_IPV4=$(hostname -I 2>/dev/null | awk '{print $1}')

# ── SMART AUTO DNS A RECORD ─────────────────────────────────────────────────
# Eğer hostname'in parent domain'i bizim PowerDNS'te yönetiliyorsa, otomatik
# A kaydı ekle. Kullanıcının registrar paneline girmesine gerek yok — panel
# kendi PDNS'inde kayıt ekler, propagation public DNS'e otomatik gider
# (nameserver delegasyonu varsa). Self-service SSL.
PARENT_DOMAIN="${HOSTNAME#*.}"
ROOT_CNF="/root/.onox-mysql-root.cnf"
AUTO_DNS_ADDED=0

if [[ -f "${ROOT_CNF}" && -n "${SERVER_IPV4}" ]]; then
    # Parent zone PowerDNS'te var mı?
    PARENT_ZONE_ID=$(mysql --defaults-file="${ROOT_CNF}" --batch --silent onoxsoft_pdns \
        -e "SELECT id FROM domains WHERE name='${PARENT_DOMAIN}' LIMIT 1;" 2>/dev/null)

    # Yoksa: hostname kendisi apex zone olabilir (örn onoxsoft.com.tr)
    if [[ -z "${PARENT_ZONE_ID}" ]]; then
        PARENT_ZONE_ID=$(mysql --defaults-file="${ROOT_CNF}" --batch --silent onoxsoft_pdns \
            -e "SELECT id FROM domains WHERE name='${HOSTNAME}' LIMIT 1;" 2>/dev/null)
        if [[ -n "${PARENT_ZONE_ID}" ]]; then
            PARENT_DOMAIN="${HOSTNAME}"
        fi
    fi

    if [[ -n "${PARENT_ZONE_ID}" ]]; then
        # A kaydı var mı, doğru IP mi?
        EXISTING_A=$(mysql --defaults-file="${ROOT_CNF}" --batch --silent onoxsoft_pdns \
            -e "SELECT content FROM records WHERE domain_id=${PARENT_ZONE_ID} AND name='${HOSTNAME}' AND type='A' LIMIT 1;" 2>/dev/null)

        if [[ "${EXISTING_A}" != "${SERVER_IPV4}" ]]; then
            mysql --defaults-file="${ROOT_CNF}" onoxsoft_pdns >/dev/null 2>&1 <<SQL
DELETE FROM records WHERE domain_id=${PARENT_ZONE_ID} AND name='${HOSTNAME}' AND type='A';
INSERT INTO records (domain_id, name, type, content, ttl, prio, disabled)
VALUES (${PARENT_ZONE_ID}, '${HOSTNAME}', 'A', '${SERVER_IPV4}', 300, NULL, 0);
UPDATE records SET content = CONCAT(SUBSTRING_INDEX(content, ' ', 2), ' ', DATE_FORMAT(NOW(), '%Y%m%d%H'), ' ', SUBSTRING_INDEX(content, ' ', -4)) WHERE domain_id=${PARENT_ZONE_ID} AND type='SOA';
SQL
            pdns_control purge "${PARENT_DOMAIN}" >/dev/null 2>&1
            pdns_control purge "${HOSTNAME}" >/dev/null 2>&1
            AUTO_DNS_ADDED=1
            sleep 2
        fi
    fi
fi

# ── DNS resolve check (multi-resolver) ──────────────────────────────────────
RESOLVED=""
RESOLVED_TYPE=""
for resolver in "" "@8.8.8.8" "@1.1.1.1" "@9.9.9.9"; do
    r4=$(dig $resolver +short +time=3 +tries=1 "${HOSTNAME}" A 2>/dev/null | grep -E '^([0-9]+\.){3}[0-9]+$' | head -1)
    if [[ -n "$r4" ]]; then RESOLVED="$r4"; RESOLVED_TYPE="A"; break; fi
    r6=$(dig $resolver +short +time=3 +tries=1 "${HOSTNAME}" AAAA 2>/dev/null | grep -E '^[0-9a-fA-F:]+$' | head -1)
    if [[ -n "$r6" ]]; then RESOLVED="$r6"; RESOLVED_TYPE="AAAA"; break; fi
done

if [[ -z "${RESOLVED}" ]]; then
    if [[ "${AUTO_DNS_ADDED}" -eq 1 ]]; then
        die "DNS A kaydı PowerDNS'e otomatik eklendi (${HOSTNAME} → ${SERVER_IPV4}) ama public DNS henüz propagate olmadı. Parent zone NS'i bu sunucuya işaret ediyor mu kontrol et: dig +short NS ${PARENT_DOMAIN}. 5-15 dakika bekleyip tekrar dene." 2
    fi
    die "DNS A kaydı yok: ${HOSTNAME}. Sunucu IPv4: ${SERVER_IPV4:-?}. ${PARENT_DOMAIN} parent domain'i bu sunucunun PowerDNS'inde değil. Çözüm: (1) ${PARENT_DOMAIN} zone'unu panel DNS'ine ekle, VEYA (2) registrar panelinde manuel A kaydı: ${HOSTNAME} → ${SERVER_IPV4}" 2
fi

mkdir -p "${SSL_DIR}"
# chmod 711: apache (PHP-FPM) user dizine traverse edebilir + cert dosyalarını
# path ile direkt okuyabilir, ama dizini listeleyemez (defense-in-depth).
# chmod 700 idi → SSL Manager UI "Sertifika yok" gösteriyordu çünkü is_readable()
# /etc/onoxsoft/ssl/<host>.crt için false döndü (apache traverse hakkı yok).
chmod 711 "${SSL_DIR}"

# ── acme.sh: account register (idempotent) ─────────────────────────────────
LOGFILE="/var/log/onoxsoft/panel-cert-${HOSTNAME}.log"
mkdir -p /var/log/onoxsoft
: > "${LOGFILE}"

"${ACME_BIN}" --register-account -m "${EMAIL}" --server letsencrypt >>"${LOGFILE}" 2>&1 || true

# ── Issue (HTTP-01, webroot mode) ──────────────────────────────────────────
# --server letsencrypt explicit (ZeroSSL rate-limit'inden kaçınma)
"${ACME_BIN}" --issue \
    --server letsencrypt \
    -d "${HOSTNAME}" \
    --webroot "${WEBROOT}" \
    --keylength 2048 \
    --force >>"${LOGFILE}" 2>&1 \
    || die "acme.sh --issue başarısız. Log: ${LOGFILE}" 3

# ── Install certs to panel SSL dir ─────────────────────────────────────────
"${ACME_BIN}" --install-cert -d "${HOSTNAME}" \
    --cert-file      "${SSL_DIR}/${HOSTNAME}.crt" \
    --key-file       "${SSL_DIR}/${HOSTNAME}.key" \
    --fullchain-file "${SSL_DIR}/${HOSTNAME}.fullchain" \
    --reloadcmd      "systemctl reload httpd" >>"${LOGFILE}" 2>&1 \
    || die "acme.sh --install-cert başarısız. Log: ${LOGFILE}" 3

chmod 600 "${SSL_DIR}/${HOSTNAME}.key"
# Cert + fullchain public bilgi (tarayıcılar açıkça indirir) — apache user
# (SSL Manager UI) is_readable() ile metadata okuyabilmeli. 644 = root yazar,
# herkes okur. Tipik cPanel/Plesk davranışı.
chmod 644 "${SSL_DIR}/${HOSTNAME}.crt" "${SSL_DIR}/${HOSTNAME}.fullchain" 2>/dev/null || true

# ── Update Apache vhost SSL paths (eğer henüz eşleşmiyorsa) ────────────────
# install.sh sırasında PANEL_HOSTNAME ile yazılmış olabilir; FQDN değişimi
# durumunda vhost güncellemesi gerekebilir (best-effort, soft-fail).
VHOST_FILE="/etc/httpd/conf.d/onoxsoft-panel.conf"
if [[ -f "${VHOST_FILE}" ]] && ! grep -q "${SSL_DIR}/${HOSTNAME}.crt" "${VHOST_FILE}"; then
    sed -i "s|SSLCertificateFile.*|SSLCertificateFile    ${SSL_DIR}/${HOSTNAME}.fullchain|" "${VHOST_FILE}" 2>/dev/null || true
    sed -i "s|SSLCertificateKeyFile.*|SSLCertificateKeyFile ${SSL_DIR}/${HOSTNAME}.key|" "${VHOST_FILE}" 2>/dev/null || true
    systemctl reload httpd 2>/dev/null || true
fi

# ── Read expiry ────────────────────────────────────────────────────────────
EXPIRES_AT=""
if [[ -f "${SSL_DIR}/${HOSTNAME}.crt" ]]; then
    EXPIRES_AT=$(openssl x509 -enddate -noout -in "${SSL_DIR}/${HOSTNAME}.crt" 2>/dev/null \
        | sed 's/^notAfter=//' \
        | xargs -I{} date -d "{}" -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "")
fi

# ── Output ─────────────────────────────────────────────────────────────────
jq -nc \
    --arg host "${HOSTNAME}" \
    --arg crt "${SSL_DIR}/${HOSTNAME}.crt" \
    --arg fullchain "${SSL_DIR}/${HOSTNAME}.fullchain" \
    --arg key "${SSL_DIR}/${HOSTNAME}.key" \
    --arg exp "${EXPIRES_AT}" \
    --arg log "${LOGFILE}" \
    '{ok:true,hostname:$host,cert_path:$crt,fullchain_path:$fullchain,key_path:$key,expires_at:$exp,issuer:"Let'\''s Encrypt",log_file:$log}'

exit 0
