#!/usr/bin/env bash
# onx-db-version-switch — Execute MySQL/MariaDB version upgrade (SAFE).
#
# v2.0 SAFETY OVERHAUL:
#   - Cross-engine (MariaDB ⇄ MySQL) REFUSED unless explicit
#     allow_cross_engine: true (Phase 2 dump+import flow gerek)
#   - Datadir snapshot before destructive ops (mv yerine cp for safety)
#   - AUTO-ROLLBACK on any failure: stop new → restore datadir → reinstall old
#   - Pre-check: target package available + datadir disk space
#
# Same-engine version jump (MariaDB 10.5 → 10.11): in-place upgrade
# Cross-engine (MariaDB → MySQL): REFUSED v2.0'da (manuel dump+import)
#
# Input (stdin JSON):
#   target_version       string   "11.4" | "8.0"
#   target_engine        string   "mariadb" | "mysql"
#   backup_path          string   Prepare adımında üretilen backup
#   confirm_token        string   Sysapi'ya gönderilen sha (idempotent guard)
#   allow_cross_engine   bool     (optional, default false) — cross-engine kabul
#
# Exit codes: 0=ok 1=invalid-input 2=preflight-fail 3=exec-fail 4=rolled-back

set -euo pipefail

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

INPUT=$(cat)
onx_require_json "${INPUT}"

TARGET_VERSION=$(onx_json_get "${INPUT}" "target_version")
TARGET_ENGINE=$(onx_json_get "${INPUT}" "target_engine")
BACKUP_PATH=$(onx_json_get "${INPUT}" "backup_path")
CONFIRM_TOKEN=$(onx_json_get "${INPUT}" "confirm_token")
ALLOW_CROSS=$(onx_json_get "${INPUT}" "allow_cross_engine" 2>/dev/null || echo "false")

[[ -z "${TARGET_VERSION}" || -z "${TARGET_ENGINE}" ]] && onx_die 1 "target_version and target_engine required"
[[ -z "${BACKUP_PATH}" ]] && onx_die 1 "backup_path is required (run onx-db-version-prepare first)"
[[ -z "${CONFIRM_TOKEN}" ]] && onx_die 1 "confirm_token is required (admin double-confirm)"
[[ -f "${BACKUP_PATH}" ]] || onx_die 2 "backup file not found: ${BACKUP_PATH}"

case "${TARGET_ENGINE}" in
    mysql|mariadb) ;;
    *) onx_die 1 "target_engine must be mysql or mariadb" ;;
esac

[[ $EUID -eq 0 ]] || onx_die 2 "must be run as root"

# ── Detect current engine ────────────────────────────────────────────────────
START_TS=$(date +%s)
TS_TAG=$(date +%Y%m%d%H%M%S)

# Engine tespit: önce binary, sonra service unit
CURRENT_ENGINE="unknown"
if command -v mariadbd >/dev/null 2>&1 || systemctl is-active mariadb >/dev/null 2>&1; then
    CURRENT_ENGINE="mariadb"
elif command -v mysqld >/dev/null 2>&1 || systemctl is-active mysqld >/dev/null 2>&1; then
    CURRENT_ENGINE="mysql"
fi

FROM_VER=$(mysql --version 2>/dev/null | grep -oE '[A-Za-z]+ [0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")

# ── SAFETY GATE: Cross-engine migration ──────────────────────────────────────
# MariaDB ↔ MySQL switch'i datadir UYUMSUZ — in-place upgrade İMKANSIZ.
# Phase 2'de proper dump+import flow eklenecek. Şu an REFUSE.
if [[ "${CURRENT_ENGINE}" != "unknown" ]] && [[ "${CURRENT_ENGINE}" != "${TARGET_ENGINE}" ]]; then
    if [[ "${ALLOW_CROSS}" != "true" ]]; then
        onx_die 2 "Cross-engine migration REFUSED: ${CURRENT_ENGINE} → ${TARGET_ENGINE}. Datadir uyumsuz, dump+import gerek (Phase 2). Aynı engine'de minor version bump deneyin (örn: MariaDB 10.5 → 10.11)."
    fi
    # Cross-engine explicitly allowed — uyari logla
    onx_log "WARNING: Cross-engine migration accepted (allow_cross_engine=true): ${CURRENT_ENGINE} → ${TARGET_ENGINE}"
fi

SERVICE_UNIT="mariadb"
[[ "${TARGET_ENGINE}" == "mysql" ]] && SERVICE_UNIT="mysqld"

OLD_SERVICE_UNIT="mariadb"
[[ "${CURRENT_ENGINE}" == "mysql" ]] && OLD_SERVICE_UNIT="mysqld"

# ── Pre-check: Disk space (datadir backup için) ──────────────────────────────
DATADIR="$(mysql --defaults-extra-file=/root/.onox-mysql-root.cnf -Nse "SHOW VARIABLES LIKE 'datadir'" 2>/dev/null | awk '{print $2}' || echo "/var/lib/mysql")"
DATADIR="${DATADIR%/}"

if [[ -d "${DATADIR}" ]]; then
    DATADIR_BYTES=$(du -sb "${DATADIR}" 2>/dev/null | awk '{print $1}' || echo 0)
    DATADIR_MB=$(( DATADIR_BYTES / 1024 / 1024 ))
    AVAIL_MB=$(df -Pm "$(dirname "${DATADIR}")" | awk 'NR==2{print $4}')
    NEEDED_MB=$(( DATADIR_MB + 500 )) # +500MB safety margin
    if [[ ${AVAIL_MB} -lt ${NEEDED_MB} ]]; then
        onx_die 2 "Insufficient disk for datadir backup: need ${NEEDED_MB}MB, have ${AVAIL_MB}MB available"
    fi
fi

DATADIR_SNAPSHOT="${DATADIR}.preupgrade-${TS_TAG}"

onx_log "DB upgrade start: ${CURRENT_ENGINE} ${FROM_VER} → ${TARGET_ENGINE} ${TARGET_VERSION} (datadir=${DATADIR}, snapshot=${DATADIR_SNAPSHOT})"

# ── Rollback function ────────────────────────────────────────────────────────
ROLLBACK_DONE=0
rollback_to_old() {
    [[ ${ROLLBACK_DONE} -eq 1 ]] && return
    ROLLBACK_DONE=1
    onx_log "ROLLBACK starting: restoring ${CURRENT_ENGINE} ${FROM_VER}"

    # Stop yeni servisi (varsa)
    systemctl stop "${SERVICE_UNIT}" 2>/dev/null || true

    # Datadir'i geri yükle
    if [[ -d "${DATADIR_SNAPSHOT}" ]]; then
        rm -rf "${DATADIR}" 2>/dev/null || true
        mv "${DATADIR_SNAPSHOT}" "${DATADIR}"
        chown -R mysql:mysql "${DATADIR}" 2>/dev/null || true
        onx_log "Datadir restored from snapshot"
    fi

    # Eski engine'i tekrar kur (paket reset edildi ise)
    if command -v dnf >/dev/null 2>&1; then
        if [[ "${CURRENT_ENGINE}" == "mariadb" ]]; then
            dnf module reset mariadb -y >/dev/null 2>&1 || true
            dnf install -y mariadb-server mariadb >/dev/null 2>&1 || true
        else
            dnf module reset mysql -y >/dev/null 2>&1 || true
            dnf install -y mysql-server >/dev/null 2>&1 || true
        fi
    fi

    # Eski servisi başlat
    systemctl start "${OLD_SERVICE_UNIT}" 2>/dev/null || true

    onx_log "ROLLBACK complete"
}

# Trap her exit'te rollback kontrolü yap (sadece error path'inde)
trap 'if [[ $? -ne 0 ]] && [[ ${ROLLBACK_DONE} -eq 0 ]]; then rollback_to_old; fi' EXIT

# ── 1. Service stop ──────────────────────────────────────────────────────────
onx_log "Step 1/6: stopping ${OLD_SERVICE_UNIT}"
systemctl stop "${OLD_SERVICE_UNIT}" 2>/dev/null || true
sleep 2 # ensure socket cleanup

# ── 2. Datadir snapshot (cp -a, slow but safe) ───────────────────────────────
# Aynı engine in-place upgrade'de datadir paylaşılır. Cross-engine için yeni
# datadir init edilir, snapshot rollback için tutulur.
if [[ -d "${DATADIR}" ]]; then
    onx_log "Step 2/6: snapshotting datadir (${DATADIR_MB}MB) → ${DATADIR_SNAPSHOT}"
    # cp -a (preserve perms/links) — büyük datadir için yavaş ama güvenli
    cp -a "${DATADIR}" "${DATADIR_SNAPSHOT}" \
        || onx_die 3 "datadir snapshot failed (rollback impossible)"
fi

# ── 3. Repo swap + install ───────────────────────────────────────────────────
onx_log "Step 3/6: package swap to ${TARGET_ENGINE} ${TARGET_VERSION}"
if command -v dnf >/dev/null 2>&1; then
    if [[ "${TARGET_ENGINE}" == "mariadb" ]]; then
        dnf module reset mariadb -y >/dev/null 2>&1 || true
        if ! dnf module install -y "mariadb:${TARGET_VERSION}" 2>/dev/null; then
            # Fallback: upstream repo. OS major'a göre rhel9/rhel10 path türet (el9 korunur).
            # NOT: el10 mirror düzeni MariaDB tarafında değişebilir (rhel/10/x86_64) — AL10
            # VM testinde doğrula; el10'da modularity yok → bu fallback yol her zaman devreye girer.
            _elmaj="$(onx_el_major)"
            cat > /etc/yum.repos.d/mariadb-onox.repo <<EOF
[mariadb-onox]
name=MariaDB ${TARGET_VERSION}
baseurl=https://mirror.mariadb.org/yum/${TARGET_VERSION}/rhel${_elmaj}-amd64
gpgkey=https://supplychain.mariadb.com/MariaDB-Server-GPG-KEY
gpgcheck=1
enabled=1
EOF
            dnf install -y MariaDB-server MariaDB-client || onx_die 3 "MariaDB install failed"
        fi
    else
        # MySQL: önce MariaDB paketlerini temizle (conflict önlemi)
        if [[ "${CURRENT_ENGINE}" == "mariadb" ]] && [[ "${ALLOW_CROSS}" == "true" ]]; then
            dnf remove -y mariadb-server mariadb mariadb-common 2>/dev/null || true
        fi
        dnf module reset mysql -y >/dev/null 2>&1 || true
        dnf module install -y "mysql:${TARGET_VERSION}" || onx_die 3 "MySQL install failed"
    fi
elif command -v apt >/dev/null 2>&1; then
    apt update >/dev/null 2>&1
    if [[ "${TARGET_ENGINE}" == "mariadb" ]]; then
        apt install -y "mariadb-server-${TARGET_VERSION}" || onx_die 3 "MariaDB install failed"
    else
        apt install -y "mysql-server-${TARGET_VERSION}" || onx_die 3 "MySQL install failed"
    fi
else
    onx_die 2 "no supported package manager (dnf/apt)"
fi

# ── 4. Datadir handling for cross-engine ─────────────────────────────────────
# Same-engine: datadir aynı kalır (in-place upgrade)
# Cross-engine: yeni datadir init, import phase 2'de
if [[ "${CURRENT_ENGINE}" != "${TARGET_ENGINE}" ]] && [[ "${ALLOW_CROSS}" == "true" ]]; then
    onx_log "Step 4/6: cross-engine — new datadir init"
    # Eski datadir snapshot'ta zaten; current'ı temizle
    rm -rf "${DATADIR:?}"/* 2>/dev/null || true

    if [[ "${TARGET_ENGINE}" == "mariadb" ]]; then
        mariadb-install-db --user=mysql --datadir="${DATADIR}" >/dev/null 2>&1 \
            || mysql_install_db --user=mysql --datadir="${DATADIR}" >/dev/null 2>&1 \
            || onx_die 3 "datadir init failed"
    else
        mysqld --initialize-insecure --user=mysql --datadir="${DATADIR}" >/dev/null 2>&1 \
            || onx_die 3 "mysqld --initialize failed"
    fi
    chown -R mysql:mysql "${DATADIR}"
fi

# ── 5. Start service ─────────────────────────────────────────────────────────
onx_log "Step 5/6: starting ${SERVICE_UNIT}"
systemctl enable --now "${SERVICE_UNIT}" 2>/dev/null \
    || systemctl enable --now mariadb 2>/dev/null \
    || systemctl enable --now mysqld 2>/dev/null \
    || onx_die 3 "failed to start ${SERVICE_UNIT} — check journalctl -u ${SERVICE_UNIT}"

# Wait for socket (mysqladmin_root — root cnf gerek)
WAIT_OK=0
for i in {1..30}; do
    if mysqladmin_root ping >/dev/null 2>&1; then
        WAIT_OK=1
        break
    fi
    sleep 1
done
[[ ${WAIT_OK} -eq 1 ]] || onx_die 3 "DB did not respond to ping within 30s"

# ── 6. mysql_upgrade (same-engine için sistem tablolarını migrate et) ────────
onx_log "Step 6/6: mysql_upgrade"
UPGRADE_OK="false"
if [[ "${CURRENT_ENGINE}" == "${TARGET_ENGINE}" ]]; then
    if command -v mariadb-upgrade >/dev/null 2>&1; then
        mariadb-upgrade --force >/dev/null 2>&1 && UPGRADE_OK="true" || true
    elif command -v mysql_upgrade >/dev/null 2>&1; then
        mysql_upgrade --force >/dev/null 2>&1 && UPGRADE_OK="true" || true
    fi
fi

# ── Verify ───────────────────────────────────────────────────────────────────
TO_VER=$(mysql --version 2>/dev/null | grep -oE '[A-Za-z]+ [0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")

# Success — disable rollback trap
trap - EXIT

# Snapshot başarıdan sonra silmiyoruz — admin manuel temizleyebilir
# (yer açmak için: rm -rf ${DATADIR_SNAPSHOT})
SNAPSHOT_STATUS="kept"
[[ ! -d "${DATADIR_SNAPSHOT}" ]] && SNAPSHOT_STATUS="missing"

END_TS=$(date +%s)
DOWNTIME=$(( END_TS - START_TS ))

jq -n \
    --argjson upgraded true \
    --arg from "${FROM_VER}" \
    --arg to "${TO_VER}" \
    --arg service_unit "${SERVICE_UNIT}" \
    --argjson mysql_upgrade_ok "${UPGRADE_OK}" \
    --argjson downtime_seconds "${DOWNTIME}" \
    --arg backup_path "${BACKUP_PATH}" \
    --arg datadir_snapshot "${DATADIR_SNAPSHOT}" \
    --arg snapshot_status "${SNAPSHOT_STATUS}" \
    '{upgraded: $upgraded, from: $from, to: $to, service_unit: $service_unit,
      mysql_upgrade_ok: $mysql_upgrade_ok, downtime_seconds: $downtime_seconds,
      backup_path: $backup_path, datadir_snapshot: $datadir_snapshot,
      snapshot_status: $snapshot_status,
      note: "Snapshot kept for manual rollback. Remove with: rm -rf <datadir_snapshot>"}'

onx_log "DB upgrade complete: ${FROM_VER} → ${TO_VER} (downtime=${DOWNTIME}s, upgrade_ok=${UPGRADE_OK}, snapshot=${DATADIR_SNAPSHOT})"
