#!/usr/bin/env bash
#
# onx-cert-rollback — acme.sh backup dizininden önceki cert versiyonuna geri dön.
# acme.sh her renew'da eski cert'i ~/.acme.sh/<domain>/backup/<timestamp>/ altına
# kopyalar. Bu script son backup'tan veya belirli tarihten cert'i restore eder.
#
# Stdin (JSON):
#   {
#     "domain": "example.com",        # zorunlu
#     "backup_id": "20260518_103045", # opsiyonel — boşsa son backup
#     "install_target": "panel" | "customer"  # opsiyonel, default customer
#   }
#
# Stdout (JSON):
#   {"ok":true,"domain":"...","restored_from":"<timestamp>","cert_path":"...","expires_at":"..."}
#
# Exit codes: 0=ok, 1=invalid input, 2=preflight (backup yok), 3=exec fail

set -uo pipefail

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

onx_json_input
DOMAIN=$(onx_json_field "domain")
BACKUP_ID=$(onx_json_field "backup_id")
INSTALL_TARGET=$(onx_json_field "install_target" "customer")

[[ -z "${DOMAIN}" ]] && onx_die 1 "domain gerekli"
[[ "${DOMAIN}" =~ ^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$ ]] \
    || onx_die 1 "geçersiz domain: ${DOMAIN}"

[[ "${INSTALL_TARGET}" =~ ^(panel|customer)$ ]] || onx_die 1 "install_target panel veya customer olmalı"

# ── acme.sh ev dizini bul ──────────────────────────────────────────────────
ACME_HOME=""
for candidate in /usr/local/onoxsoft/acme.sh /etc/onoxsoft/acme.sh /root/.acme.sh /opt/onoxsoft/acme.sh; do
    if [[ -d "${candidate}" && -d "${candidate}/${DOMAIN}" ]]; then
        ACME_HOME="${candidate}"
        break
    fi
done

[[ -z "${ACME_HOME}" ]] && onx_die 2 "acme.sh için ${DOMAIN} cert dizini bulunamadı"

# acme.sh backup formatı: ~/.acme.sh/<domain>/backup/<timestamp>/
# VEYA: acme.sh tüm önceki cert'i ~/.acme.sh/<domain>/ altında *.bak ile tutar
# (versiyona göre değişebilir). İkisini de kontrol edelim.

BACKUP_DIR="${ACME_HOME}/${DOMAIN}/backup"
DOMAIN_DIR="${ACME_HOME}/${DOMAIN}"

# ── Mevcut backup'ları listele ─────────────────────────────────────────────
declare -a AVAILABLE_BACKUPS=()

if [[ -d "${BACKUP_DIR}" ]]; then
    # Klasik backup/timestamp formatı
    while IFS= read -r -d '' dir; do
        AVAILABLE_BACKUPS+=("$(basename "${dir}")")
    done < <(find "${BACKUP_DIR}" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null | sort -rz)
fi

# Alternatif: <domain>/<timestamp>/ klasörleri (yeni acme.sh)
if [[ ${#AVAILABLE_BACKUPS[@]} -eq 0 ]]; then
    while IFS= read -r -d '' dir; do
        local_name=$(basename "${dir}")
        # Timestamp formatlı klasör (YYYYMMDD_HHMMSS) ise ekle
        if [[ "${local_name}" =~ ^[0-9]{8}_[0-9]{6}$ || "${local_name}" =~ ^[0-9]{14}$ ]]; then
            AVAILABLE_BACKUPS+=("${local_name}")
        fi
    done < <(find "${DOMAIN_DIR}" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null | sort -rz)
fi

# ── *.bak fallback (acme.sh dahili backup) ─────────────────────────────────
HAS_BAK_FALLBACK=0
if [[ ${#AVAILABLE_BACKUPS[@]} -eq 0 ]]; then
    if [[ -f "${DOMAIN_DIR}/fullchain.cer.bak" || -f "${DOMAIN_DIR}/${DOMAIN}.cer.bak" ]]; then
        HAS_BAK_FALLBACK=1
        AVAILABLE_BACKUPS+=("_internal_bak")
    fi
fi

[[ ${#AVAILABLE_BACKUPS[@]} -eq 0 ]] && onx_die 2 "Backup bulunamadı: ${BACKUP_DIR}"

# ── Hedef backup belirle ───────────────────────────────────────────────────
if [[ -z "${BACKUP_ID}" ]]; then
    BACKUP_ID="${AVAILABLE_BACKUPS[0]}"  # En yenisi (sort -r ile)
fi

# Listede var mı doğrula
FOUND_BACKUP=""
for b in "${AVAILABLE_BACKUPS[@]}"; do
    if [[ "${b}" == "${BACKUP_ID}" ]]; then
        FOUND_BACKUP="${b}"
        break
    fi
done
[[ -z "${FOUND_BACKUP}" ]] && onx_die 1 "Geçersiz backup_id: ${BACKUP_ID}. Mevcut: ${AVAILABLE_BACKUPS[*]}"

# ── Source dosyaları belirle ───────────────────────────────────────────────
if [[ "${FOUND_BACKUP}" == "_internal_bak" ]]; then
    SRC_FULLCHAIN="${DOMAIN_DIR}/fullchain.cer.bak"
    SRC_CERT="${DOMAIN_DIR}/${DOMAIN}.cer.bak"
    SRC_KEY="${DOMAIN_DIR}/${DOMAIN}.key.bak"
else
    # backup/<id>/ veya <domain>/<id>/
    if [[ -d "${BACKUP_DIR}/${FOUND_BACKUP}" ]]; then
        SRC_BASE="${BACKUP_DIR}/${FOUND_BACKUP}"
    else
        SRC_BASE="${DOMAIN_DIR}/${FOUND_BACKUP}"
    fi
    SRC_FULLCHAIN="${SRC_BASE}/fullchain.cer"
    SRC_CERT="${SRC_BASE}/${DOMAIN}.cer"
    SRC_KEY="${SRC_BASE}/${DOMAIN}.key"
    # acme.sh bazı versiyonlarda farklı isimler kullanır
    [[ ! -f "${SRC_CERT}" ]] && SRC_CERT="${SRC_BASE}/cert.pem"
    [[ ! -f "${SRC_KEY}"  ]] && SRC_KEY="${SRC_BASE}/key.pem"
fi

[[ -f "${SRC_FULLCHAIN}" ]] || onx_die 3 "Backup fullchain yok: ${SRC_FULLCHAIN}"
[[ -f "${SRC_KEY}" ]] || onx_die 3 "Backup key yok: ${SRC_KEY}"

# ── Install target dizini ──────────────────────────────────────────────────
case "${INSTALL_TARGET}" in
    panel)
        DEST_DIR="/etc/onoxsoft/ssl"
        ;;
    customer)
        DEST_DIR="/etc/onoxsoft/customer-ssl"
        ;;
esac

mkdir -p "${DEST_DIR}"
chmod 711 "${DEST_DIR}"

DEST_CERT="${DEST_DIR}/${DOMAIN}.crt"
DEST_KEY="${DEST_DIR}/${DOMAIN}.key"
DEST_FULLCHAIN="${DEST_DIR}/${DOMAIN}.fullchain"

# ── Rollback öncesi mevcut cert'i .pre-rollback olarak yedekle ─────────────
ROLLBACK_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
PRE_ROLLBACK_DIR="${ACME_HOME}/${DOMAIN}/.pre-rollback-${ROLLBACK_TIMESTAMP}"
mkdir -p "${PRE_ROLLBACK_DIR}"
[[ -f "${DEST_CERT}" ]]      && cp -p "${DEST_CERT}"      "${PRE_ROLLBACK_DIR}/"
[[ -f "${DEST_KEY}" ]]       && cp -p "${DEST_KEY}"       "${PRE_ROLLBACK_DIR}/"
[[ -f "${DEST_FULLCHAIN}" ]] && cp -p "${DEST_FULLCHAIN}" "${PRE_ROLLBACK_DIR}/"

# ── Restore ────────────────────────────────────────────────────────────────
# fullchain → .fullchain
cp -f "${SRC_FULLCHAIN}" "${DEST_FULLCHAIN}"
# Cert (fullchain'in ilk cert'i veya separate)
if [[ -f "${SRC_CERT}" ]]; then
    cp -f "${SRC_CERT}" "${DEST_CERT}"
else
    # Fullchain'den ilk cert'i ayıkla
    awk '/BEGIN CERTIFICATE/{f=1} f{print} /END CERTIFICATE/{exit}' "${SRC_FULLCHAIN}" > "${DEST_CERT}"
fi
cp -f "${SRC_KEY}" "${DEST_KEY}"

chmod 600 "${DEST_KEY}"
chmod 644 "${DEST_CERT}" "${DEST_FULLCHAIN}"

# ── Apache reload ──────────────────────────────────────────────────────────
systemctl reload httpd 2>/dev/null || true

# ── Expiry oku ─────────────────────────────────────────────────────────────
EXPIRES_AT=""
NOT_AFTER=$(openssl x509 -enddate -noout -in "${DEST_CERT}" 2>/dev/null | sed 's/^notAfter=//')
[[ -n "${NOT_AFTER}" ]] && EXPIRES_AT=$(date -d "${NOT_AFTER}" -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "")

# Available backups listesi (UI'ya geri dönüş için)
BACKUPS_JSON=$(printf '%s\n' "${AVAILABLE_BACKUPS[@]}" | jq -R . | jq -s -c .)

jq -nc \
    --arg dom "${DOMAIN}" \
    --arg bid "${FOUND_BACKUP}" \
    --arg cert "${DEST_CERT}" \
    --arg key "${DEST_KEY}" \
    --arg chain "${DEST_FULLCHAIN}" \
    --arg exp "${EXPIRES_AT}" \
    --arg pre "${PRE_ROLLBACK_DIR}" \
    --argjson backups "${BACKUPS_JSON}" \
    '{
        ok: true,
        domain: $dom,
        restored_from: $bid,
        cert_path: $cert,
        key_path: $key,
        fullchain_path: $chain,
        expires_at: ($exp | if . == "" then null else . end),
        pre_rollback_backup: $pre,
        available_backups: $backups
    }'

exit 0
