#!/bin/bash
# Aktualizacja nivato-logger z repo APT, potem reboot całego urządzenia.
#
# Użycie: apt-self-upgrade [install]
# (stare aliasy download|apply też działają jak install)
#
# Exit: 0 = OK (+ reboot zaplanowany), 10 = już aktualne, inne = błąd.
#
# Uwaga: bez pipefail — w połączeniu z `exec > >(tee …)` kod 100 z apt-get update
# potrafił ubić cały skrypt zanim doszliśmy do porównania wersji.
set -eu

MODE="${1:-install}"
case "$MODE" in
    install|download|apply) ;;
    *)
        echo "Użycie: $0 [install]" >&2
        exit 64
        ;;
esac

export DEBIAN_FRONTEND=noninteractive
export NIVATO_SKIP_REBOOT=1
PACKAGE="nivato-logger"
DATA_DIR="${NIVATO_DATA_DIR:-/opt/nivato-logger/data}"
REBOOT_MARKER="$DATA_DIR/update-reboot-at"
REBOOT_DELAY_SEC=10
LOG_DIR="/opt/nivato-logger/logs"
LOG_FILE="$LOG_DIR/apt-self-upgrade.log"

# Poza cgroup nivato-logger.service (MemoryMax) — inaczej Java+apt → OOM.
if [ "${NIVATO_APT_UPGRADE_TRANSIENT:-}" != "1" ]; then
    systemctl reset-failed nivato-logger-apt-upgrade.service 2>/dev/null || true
    systemctl stop nivato-logger-apt-upgrade.service 2>/dev/null || true
    exec /usr/bin/systemd-run --wait --pipe --collect \
        --unit=nivato-logger-apt-upgrade \
        -p Type=oneshot \
        -p TimeoutStartSec=1800 \
        --setenv=NIVATO_APT_UPGRADE_TRANSIENT=1 \
        --setenv=NIVATO_SKIP_REBOOT=1 \
        --setenv=NIVATO_DATA_DIR="$DATA_DIR" \
        --setenv=DEBIAN_FRONTEND=noninteractive \
        "$0" install
fi

mkdir -p "$LOG_DIR" "$DATA_DIR"
exec > >(tee -a "$LOG_FILE") 2>&1

# shellcheck source=/dev/null
. /opt/nivato-logger/bin/apt-ensure-keyring

ensure_sources() {
    if ! nivato_ensure_apt_keyring_and_sources; then
        echo "Nie udało się przygotować klucza/źródła APT" >&2
        exit 2
    fi
}

pkg_version() {
    dpkg-query -W -f='${Version}' "$PACKAGE" 2>/dev/null || echo ""
}

candidate_version() {
    apt-cache policy "$PACKAGE" 2>/dev/null | awk '/Candidate:/ { print $2; exit }' || true
}

wait_for_apt_locks() {
    local i=0
    while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 \
        || fuser /var/lib/dpkg/lock >/dev/null 2>&1 \
        || fuser /var/lib/apt/lists/lock >/dev/null 2>&1 \
        || fuser /var/cache/apt/archives/lock >/dev/null 2>&1; do
        i=$((i + 1))
        if [ "$i" -gt 90 ]; then
            echo "Timeout czekania na blokadę APT/dpkg" >&2
            exit 5
        fi
        echo "Czekam na zwolnienie blokady APT ($i)…"
        sleep 2
    done
}

exit_up_to_date() {
    local ver="${1:-}"
    echo "STATUS=UP_TO_DATE"
    echo "Pakiet jest już aktualny (${ver:-brak})"
    rm -f "$REBOOT_MARKER" "$DATA_DIR/update-ready-version"
    exit 10
}

ensure_sources
wait_for_apt_locks

BEFORE="$(pkg_version)"
echo "Zainstalowana wersja: ${BEFORE:-brak}"

# Cache przed update — gdy update padnie (kod 100), i tak możemy stwierdzić „już aktualny”.
CANDIDATE_CACHE="$(candidate_version)"
echo "Kandydat (cache): ${CANDIDATE_CACHE:-brak}"

# Nigdy nie dziedzicz kodu wyjścia apt-get update (często 100 przy ostrzeżeniach/listach).
UPDATE_RC=0
set +e
/usr/bin/apt-get update -o Acquire::Retries=3
UPDATE_RC=$?
set -e
if [ "$UPDATE_RC" -ne 0 ]; then
    echo "Ostrzeżenie: apt-get update zakończone kodem $UPDATE_RC — kontynuuję z cache"
fi

CANDIDATE="$(candidate_version)"
if [ -z "$CANDIDATE" ] || [ "$CANDIDATE" = "(none)" ]; then
    CANDIDATE="$CANDIDATE_CACHE"
fi
echo "Kandydat w repo: ${CANDIDATE:-brak}"

if [ -z "$CANDIDATE" ] || [ "$CANDIDATE" = "(none)" ]; then
    echo "Brak kandydata pakietu $PACKAGE w repozytorium (sprawdź sieć i https://apt.nivato.app/)" >&2
    exit 2
fi

if [ -n "$BEFORE" ] && dpkg --compare-versions "$BEFORE" ge "$CANDIDATE"; then
    exit_up_to_date "$BEFORE"
fi

wait_for_apt_locks
echo "Instaluję ${PACKAGE}=${CANDIDATE}…"
INSTALL_RC=0
set +e
/usr/bin/apt-get install -y --allow-change-held-packages \
    -o Dpkg::Options::=--force-confdef \
    -o Dpkg::Options::=--force-confold \
    "${PACKAGE}=${CANDIDATE}"
INSTALL_RC=$?
set -e

if [ "$INSTALL_RC" -ne 0 ]; then
    AFTER_FAIL="$(pkg_version)"
    if [ -n "$AFTER_FAIL" ] && dpkg --compare-versions "$AFTER_FAIL" ge "$CANDIDATE"; then
        exit_up_to_date "$AFTER_FAIL"
    fi
    echo "apt-get install zakończone kodem $INSTALL_RC" >&2
    exit "$INSTALL_RC"
fi

AFTER="$(pkg_version)"
echo "Wersja po aktualizacji: ${AFTER:-brak}"

if [ -z "$AFTER" ]; then
    echo "Pakiet nie jest zainstalowany po apt-get install" >&2
    exit 3
fi

if [ -n "$BEFORE" ] && [ "$BEFORE" = "$AFTER" ]; then
    # Ten sam numer wersji po „udanej” instalacji = już było aktualne
    exit_up_to_date "$AFTER"
fi

rm -f "$DATA_DIR/update-ready-version"
echo "Aktualizacja OK: ${BEFORE:-none} -> $AFTER"

reboot_at=$(( $(date +%s) + REBOOT_DELAY_SEC ))
printf '%s\n' "$reboot_at" > "$REBOOT_MARKER"
chmod 644 "$REBOOT_MARKER"
echo "Restart urządzenia zaplanowany za ${REBOOT_DELAY_SEC}s (marker $REBOOT_MARKER)"

systemctl reset-failed nivato-logger-update-reboot.service 2>/dev/null || true
systemctl stop nivato-logger-update-reboot.service 2>/dev/null || true
if ! systemd-run --unit=nivato-logger-update-reboot --on-active="${REBOOT_DELAY_SEC}s" /sbin/reboot \
        >/dev/null 2>&1; then
    (sleep "$REBOOT_DELAY_SEC"; /sbin/reboot) >/dev/null 2>&1 &
fi

exit 0
