#!/bin/bash
# EVI Platform Installation Script for 1.5.0

set -euo pipefail

export LC_ALL=C.UTF-8
export LANG=C.UTF-8
unset LANGUAGE

set +u
if [ "$USER" != "root" ] && [ "$(whoami)" != "root" ]; then
    echo "[EROR] To execute the script you need sudo permissions, run the script with superuser permissions"
    exit 1
fi
set -u

for arg in "$@"; do
    if [[ "$arg" =~ ^([A-Za-z_][A-Za-z_0-9]*)=(.*)$ ]]; then
        declare -g "${BASH_REMATCH[1]}=${BASH_REMATCH[2]}"
    fi
done

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

########################################################################
# Global variables

EVI_VERSION="1.5.0"

STATE_DIR="${SCRIPT_DIR}/.state"
FLAG_REBOOT_NEEDED="${STATE_DIR}/reboot_pending"
FLAG_DRIVER_OK="${STATE_DIR}/driver_ok"
FLAG_DB_DROPPED="${STATE_DIR}/db_dropped"
FLAG_MIGRATED="${STATE_DIR}/migrated"
FLAG_ANALYTICS_TYPE="${STATE_DIR}/analytics_type"
FLAG_REBOOTED="${STATE_DIR}/rebooted"
DOWNLOAD_DIR="${SCRIPT_DIR}/downloads"

export STATE_DIR DOWNLOAD_DIR FLAG_REBOOT_NEEDED FLAG_DRIVER_OK FLAG_DB_DROPPED FLAG_MIGRATED FLAG_ANALYTICS_TYPE FLAG_REBOOTED

: "${BACKUP_DIR_POSTGRESQL:=${SCRIPT_DIR}/backups}"
: "${BACKUP_DIR_CLICKHOUSE:=/var/backups/evi-clickhouse}"
export BACKUP_DIR_POSTGRESQL BACKUP_DIR_CLICKHOUSE

ANALYTICS_TYPE=""
export ANALYTICS_TYPE

EVI_FOUND_PACKAGES=()
export EVI_FOUND_PACKAGES

EVI_CURRENT_PACKAGES=()
export EVI_CURRENT_PACKAGES

EVI_ANALYTICS_DOCKER_FOUND=0
export EVI_ANALYTICS_DOCKER_FOUND

EVI_ANALYTICS_DOCKER_CURRENT=0
export EVI_ANALYTICS_DOCKER_CURRENT

########################################################################
# Utilities

# Background process progress indicator.
# || true: non-zero exit from bg process does not trigger set -e;
# caller checks result via _pkg_is_installed / rpm -q / etc.
_wait_bg() {
    local bg_pid=$!
    local _xtrace=0
    [[ "$-" == *x* ]] && _xtrace=1 && set +x

    local first=1 i=0
    while kill -0 "${bg_pid}" 2>/dev/null; do
        i=$(( i % 3 + 1 ))
        [[ $first -eq 1 ]] && first=0 || printf '\b\b\b'
        case $i in
            1) printf '.  ' ;;
            2) printf '.. ' ;;
            3) printf '...' ;;
        esac
        sleep 0.5
    done
    printf '\r\033[2K'
    wait "${bg_pid}" || true

    [[ $_xtrace -eq 1 ]] && set -x
    return 0
}

_detect_extern_host() {
    local host=""

    if command -v ip &>/dev/null; then
        local iface
        iface=$(ip ro get fibmatch 0.0.0.1 2>/dev/null \
            | grep -o 'dev [^ ]\+' | awk '{print $2}')
        if [[ -n "$iface" ]]; then
            host=$(ip -br -4 a show "$iface" 2>/dev/null \
                | grep -oP '\d+\.\d+\.\d+\.\d+' | head -1)
        fi
    fi

    if [[ -z "$host" ]] && command -v hostname &>/dev/null; then
        host=$(hostname -I 2>/dev/null | awk '{print $1}')
    fi

    if [[ -z "$host" ]]; then
        echo "[WARN] Could not detect external IP automatically - using 127.0.0.1 as fallback." >&2
        echo "[INFO] Override: sudo bash evi-${EVI_VERSION}_install.sh EVI_LIVE_EXTERN_HOST=<your-ip>" >&2
        host="127.0.0.1"
    fi

    echo "$host"
}

########################################################################
# TUI and hardware detection

_ensure_whiptail() {
    if command -v whiptail &>/dev/null; then
        return 0
    fi

    echo "[INFO] Installing whiptail..."
    if command -v apt &>/dev/null; then
        DEBIAN_FRONTEND=noninteractive apt install -y -qqqq whiptail 2>/dev/null
    elif command -v dnf &>/dev/null; then
        dnf install -y -qqqq newt 2>/dev/null
    fi

    if ! command -v whiptail &>/dev/null; then
        echo "[EROR] whiptail is not available and could not be installed."
        echo "[INFO] Ubuntu/Astra: apt install whiptail"
        echo "[INFO] RedOS:        dnf install newt"
        return 1
    fi

    echo "[INFO] whiptail installed"
}

# Sets VIDEOCLIENT=0|1 and VIDEOCARD=0|1.
_detect_gpu() {
    if ! command -v Xorg &>/dev/null && ! command -v Xwayland &>/dev/null; then
        VIDEOCLIENT=0
    else
        VIDEOCLIENT=1
    fi

    if nvidia-smi &>/dev/null 2>&1; then
        VIDEOCARD=1
        echo "[INFO] NVIDIA GPU detected (nvidia-smi)"
    elif command -v lspci &>/dev/null && lspci 2>/dev/null | grep -qi "NVIDIA"; then
        VIDEOCARD=1
        echo "[INFO] NVIDIA GPU detected (lspci)"
    elif grep -rql "0x10de" /sys/bus/pci/devices/*/vendor 2>/dev/null; then
        VIDEOCARD=1
        echo "[INFO] NVIDIA GPU detected (/sys/bus/pci vendor 0x10de)"
    else
        VIDEOCARD=0
        echo "[WARN] NVIDIA GPU not detected (checked: nvidia-smi, lspci, /sys/bus/pci)"
    fi
}

_show_components_menu() {
    if ! command -v whiptail &>/dev/null; then
        echo "[EROR] whiptail is not available. Cannot show interactive menu."
        exit 1
    fi

    local title="EVI PLATFORM ${EVI_VERSION}"
    local prompt=$'
Controls:
* Arrow keys - navigate
* Space      - toggle
* Tab        - switch focus
* Enter      - confirm

Select the components to install:'

    if [[ -f "${FLAG_REBOOT_NEEDED}" ]]; then
        SELECTED_COMPONENTS=$(whiptail --title "$title" \
            --checklist \
            --notags \
            "$prompt" \
            22 37 8 \
            evi-core         "EVI-CORE"         0 \
            evi-core-web     "EVI-CORE-WEB"     0 \
            evi-scud         "EVI-SCUD"         0 \
            evi-live         "EVI-PERIMETER"    0 \
            evi-analyzer     "EVI-ANALYZER"     0 \
            evi-archive      "EVI-ARCHIVE"      0 \
            evi-video-client "EVI-VIDEO-CLIENT" 0 \
            evi-analytics    "EVI-ANALYTICS"    1 \
            3>&1 1>&2 2>&3) || SELECTED_COMPONENTS=""
    else
        SELECTED_COMPONENTS=$(whiptail --title "$title" \
            --checklist \
            --notags \
            "$prompt" \
            22 37 8 \
            evi-core         "EVI-CORE"         1 \
            evi-core-web     "EVI-CORE-WEB"     1 \
            evi-scud         "EVI-SCUD"         1 \
            evi-live         "EVI-PERIMETER"    1 \
            evi-analyzer     "EVI-ANALYZER"     1 \
            evi-archive      "EVI-ARCHIVE"      1 \
            evi-video-client "EVI-VIDEO-CLIENT" "$VIDEOCLIENT" \
            evi-analytics    "EVI-ANALYTICS"    "$VIDEOCARD" \
            3>&1 1>&2 2>&3) || SELECTED_COMPONENTS=""
    fi

    SELECTED_COMPONENTS="${SELECTED_COMPONENTS//\"/}"
}

# Sets ANALYTICS_TYPE=cpu|gpu or empty.
_show_analytics_menu() {
    local title="EVI PLATFORM ${EVI_VERSION} - Analytics type"
    local choice
    choice=$(whiptail --title "$title" \
        --menu "
GPU requirements:
* 4 GB VRAM
* Architecture Turing or newer (Compute Capability 7.5+)

Example: consumer cards from GTX 1650 and higher.

Select analytics type:" \
        18 65 2 \
        "cpu" "Use CPU (software)" \
        "gpu" "Use GPU (NVIDIA hardware)" \
        3>&1 1>&2 2>&3) || choice=""

    if [[ -z "$choice" ]]; then
        echo "[EROR] Analytics type selection cancelled."
        exit 0
    fi

    if [[ "$choice" == "gpu" ]] && [[ "$VIDEOCARD" -eq 0 ]]; then
        local switch_choice=0
        whiptail --title "$title" \
            --yes-button "Install CPU Analytics" \
            --no-button  "Skip" \
            --yesno "X NVIDIA GPU not found.

Install CPU Analytics instead of GPU?

If you select <Skip>, EVI Analytics will NOT be installed." \
            11 71 || switch_choice=$?
        if [[ $switch_choice -eq 0 ]]; then
            choice="cpu"
        elif [[ $switch_choice -eq 255 ]]; then
            echo "[EROR] Analytics type selection cancelled."
            exit 0
        else
            echo "[WARN] EVI Analytics installation skipped (no GPU detected, analytics cancelled)."
            ANALYTICS_TYPE=""
            export ANALYTICS_TYPE
            return 0
        fi
    fi

    ANALYTICS_TYPE="$choice"
    export ANALYTICS_TYPE
}

########################################################################
# Package management

# Package manager adapters (overridden by _load_deb / _load_rpm)
_pkg_install()      { echo "[EROR] _pkg_install not implemented"; exit 1; }
_pkg_remove()       { echo "[EROR] _pkg_remove not implemented"; exit 1; }
_pkg_autoremove()   { :; }
_pkg_is_installed() { return 1; }
_pkg_get_version()  { echo ""; }

# Web server hooks (overridden by distro loaders)
_install_web_server() { :; }
_post_install_web_server() { :; }

_download_evi_packages() {
    local tarball="${EVI_TARBALL}"
    mkdir -p "${DOWNLOAD_DIR}"

    echo "[INFO] Downloading EVI ${EVI_VERSION} packages..."
    if ! wget -q --show-progress --no-check-certificate \
            "https://archive.eltex-co.ru/evi-raw/evi-${EVI_VERSION}/${tarball}" \
            -O - | tar -xf - -C "${DOWNLOAD_DIR}"; then
        echo "[EROR] Failed to download ${tarball} from https://archive.eltex-co.ru"
        exit 1
    fi
    echo "[INFO] EVI ${EVI_VERSION} packages downloaded and extracted"
}

_detect_analytics_docker() {
    if ! command -v docker &>/dev/null; then
        return 0
    fi

    local analytics_line
    analytics_line=$(docker ps -a --format "{{.Names}}\t{{.Image}}" 2>/dev/null \
        | grep -P "^evi-analytics\t" || true)
    if [[ -n "$analytics_line" ]]; then
        local analytics_image="${analytics_line##*$'\t'}"
        echo "[INFO] Found evi-analytics Docker container (image: ${analytics_image:-unknown})"
        if [[ "${analytics_image:-}" == *"${EVI_VERSION}"* ]]; then
            EVI_ANALYTICS_DOCKER_CURRENT=1
        else
            EVI_ANALYTICS_DOCKER_FOUND=1
        fi
    fi
}

_detect_evi_packages() {
    local evi_packages=("evi-core" "evi-core-web" "evi-scud" "evi-live" "evi-archive" "evi-analyzer" "evi-video-client")

    echo "[INFO] Searching for installed EVI packages..."

    for pkg in "${evi_packages[@]}"; do
        if ! _pkg_is_installed "$pkg"; then
            continue
        fi

        local ver
        ver=$(_pkg_get_version "$pkg")

        if [[ "$ver" == "${EVI_VERSION}" ]]; then
            EVI_CURRENT_PACKAGES+=("${pkg}=${ver}")
            continue
        fi

        EVI_FOUND_PACKAGES+=("${pkg}=${ver}")
    done

    _detect_analytics_docker
}

_remove_old_packages() {
    if [[ ${#EVI_FOUND_PACKAGES[@]} -gt 0 ]]; then
        local pkg_names=()
        for item in "${EVI_FOUND_PACKAGES[@]}"; do
            pkg_names+=("${item%%=*}")
        done
        echo "[INFO] Removing old EVI packages: ${pkg_names[*]}..."
        _pkg_remove "${pkg_names[@]}"
        _pkg_autoremove
        echo "[INFO] Old packages removed"
    fi

    if [[ "${EVI_ANALYTICS_DOCKER_FOUND}" -eq 1 ]]; then
        _remove_analytics_docker
    fi
}

# $1 - package name. Looks for local file in DOWNLOAD_DIR first.
_install_package() {
    local pkg="$1"
    local target="$pkg"

    local found
    found=$(find "${DOWNLOAD_DIR}" -maxdepth 1 -name "${pkg}*.${PKG_EXT}" 2>/dev/null \
        | grep -m1 -P "/${pkg}(-|_)${EVI_VERSION//./\\.}[^/]*\.${PKG_EXT}$") || true

    if [[ -n "$found" ]]; then
        target="${found}"
        echo "[INFO] Using local .${PKG_EXT}: $(basename "${found}")"
    fi

    export EVI_LIVE_EXTERN_HOST

    echo -n "[INFO] Installing ${pkg}"
    { _pkg_install "${target}"; } &
    _wait_bg

    if _pkg_is_installed "${pkg}"; then
        echo "[INFO] Package '${pkg}' installed"
        echo ""
        return 0
    else
        echo "[EROR] Package '${pkg}' installation error"
        echo ""
        return 1
    fi
}

########################################################################
# Database operations

# PostgreSQL adapters (overridden by _load_astra_1_8 for Docker PG)
_pg_dump_cmd()   { sudo -u postgres pg_dump "$@"; }
_pg_admin_psql() { sudo -u postgres psql "$@"; }

_pg_drop_core() {
    if _pg_admin_psql --quiet \
        -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='core' AND pid <> pg_backend_pid();" \
        -c "DROP DATABASE IF EXISTS core;" \
        -c "DROP ROLE IF EXISTS core;" \
        > /dev/null 2>&1; then
        echo "[INFO] PostgreSQL databases dropped"
    else
        echo "[EROR] PostgreSQL drop failed"
        return 1
    fi
}

_ch_wait_ready() {
    echo -n "[INFO] Waiting for ClickHouse"
    local retries=30
    while ! clickhouse-client --user=default --password=core \
            --query="SELECT 1" &>/dev/null; do
        echo -n "."
        sleep 1
        retries=$(( retries - 1 ))
        if [[ $retries -le 0 ]]; then
            echo ""
            echo "[EROR] ClickHouse not ready after 30s"
            exit 1
        fi
    done
    echo ""
}

_configure_pg_core_db() {
    systemctl stop evi-archive evi-analyzer evi-live evi-scud evi-core &>/dev/null || true

    if ! systemctl is-active --quiet "${PG_SERVICE}"; then
        echo "[EROR] PostgreSQL service is not running"
        exit 1
    fi

    if sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='core'" \
            2>/dev/null | grep -q 1; then
        echo "[INFO] PostgreSQL database 'core' already exists - skipping creation"
    else
        echo "[INFO] Creating database and role core..."
        if ! sudo -u postgres psql --quiet \
            -c "DROP DATABASE IF EXISTS core;" \
            -c "CREATE DATABASE core;" \
            -c "DROP ROLE IF EXISTS core;
                CREATE ROLE core LOGIN PASSWORD 'core';
                ALTER USER core WITH PASSWORD 'core';
                GRANT ALL ON DATABASE core TO core;
                ALTER DATABASE core OWNER TO core;
                GRANT ALL PRIVILEGES ON DATABASE core TO core;
                GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO core;
                ALTER ROLE core WITH LOGIN;
                GRANT postgres TO core;" \
            > /dev/null 2>&1; then
            echo "[EROR] Failed to create PostgreSQL database/role 'core'"
            exit 1
        fi
        echo "[INFO] PostgreSQL database and role created"
    fi
}

# Requires PG_HBA, PG_CONF, EVI_LIVE_EXTERN_HOST.
_configure_pg_network() {
    if ! grep -q "172.16.0.0/12" "${PG_HBA}" 2>/dev/null; then
        sed -i '/# IPv4 local connections:/a host    all             all             172.16.0.0/12           md5' \
            "${PG_HBA}"
    fi
    local target_listen="listen_addresses = 'localhost, ${EVI_LIVE_EXTERN_HOST}'"
    if ! grep -qF "${target_listen}" "${PG_CONF}" 2>/dev/null; then
        sed -i -E "s/^#?listen_addresses = .*/${target_listen}/" "${PG_CONF}"
    fi
}

_configure_clickhouse() {
    echo "[INFO] Configuring ClickHouse..."
    systemctl enable clickhouse-server.service > /dev/null 2>&1 || true
    systemctl stop clickhouse-server.service 2>/dev/null || true
    sed -i 's|<password></password>|<password>core</password>|g' \
        /etc/clickhouse-server/users.xml
    systemctl start clickhouse-server.service \
        || { echo "[EROR] Failed to start clickhouse-server"; exit 1; }

    _ch_wait_ready

    systemctl stop evi-archive evi-analyzer evi-live evi-scud evi-core &>/dev/null || true

    clickhouse-client --user=default --password=core \
        --query="CREATE USER IF NOT EXISTS core IDENTIFIED WITH sha256_password BY 'core'" \
        || { echo "[EROR] Failed to create ClickHouse user 'core'"; exit 1; }
    clickhouse-client --user=default --password=core \
        --query="GRANT CURRENT GRANTS on *.* TO core WITH GRANT OPTION" \
        || { echo "[EROR] Failed to grant ClickHouse privileges to 'core'"; exit 1; }
    echo "[INFO] ClickHouse configured"
}

_backup_databases() {
    local ts
    ts=$(date +%Y%m%d_%H%M%S)
    mkdir -p "${BACKUP_DIR_POSTGRESQL}"
    mkdir -p "${BACKUP_DIR_CLICKHOUSE}"
    chown clickhouse:clickhouse "${BACKUP_DIR_CLICKHOUSE}" 2>/dev/null || true

    echo "[INFO] Creating database backup (${ts})..."

    local pg_ok=0
    local pg_backup="${BACKUP_DIR_POSTGRESQL}/pg_core_${ts}.sql"
    if _pg_dump_cmd core > "${pg_backup}" 2>/dev/null; then
        echo "[INFO] PostgreSQL backup: ${pg_backup}"
        pg_ok=1
    else
        echo "[EROR] PostgreSQL backup failed (service may not be running)"
        rm -f "${pg_backup}"
    fi

    local ch_ok=0
    if command -v clickhouse-client &>/dev/null; then
        local ch_backup_zip="ch_core_${ts}.zip"
        local ch_cfg="/etc/clickhouse-server/config.d/backup_disk.xml"

        mkdir -p /etc/clickhouse-server/config.d
        cat > "${ch_cfg}" <<EOF
<?xml version="1.0"?>
<clickhouse>
    <storage_configuration>
        <disks>
            <backups><type>local</type><path>${BACKUP_DIR_CLICKHOUSE}/</path></backups>
        </disks>
    </storage_configuration>
    <backups>
        <allowed_disk>backups</allowed_disk>
        <allowed_path>${BACKUP_DIR_CLICKHOUSE}/</allowed_path>
    </backups>
</clickhouse>
EOF
        systemctl restart clickhouse-server > /dev/null 2>&1 \
            || { echo "[EROR] Failed to restart clickhouse-server"; exit 1; }

        _ch_wait_ready

        if clickhouse-client --user=default --password=core \
                --query="BACKUP DATABASE core TO Disk('backups', '${ch_backup_zip}');" \
                > /dev/null 2>&1; then
            echo "[INFO] ClickHouse backup: ${BACKUP_DIR_CLICKHOUSE}/${ch_backup_zip}"
            ch_ok=1
        else
            echo "[EROR] ClickHouse backup failed (service may not be running)"
        fi
    else
        ch_ok=1
    fi

    if [[ $pg_ok -eq 0 ]] || [[ $ch_ok -eq 0 ]]; then
        echo "[EROR] Database backup failed — aborting to prevent data loss."
        echo "[INFO] Fix the database services and re-run the installer."
        exit 1
    fi
}

_drop_databases() {
    echo "[INFO] Dropping EVI databases..."

    local evi_services=("evi-core" "evi-scud" "evi-live" "evi-archive" "evi-analyzer")
    systemctl stop "${evi_services[@]}" &>/dev/null || true

    _pg_drop_core || exit 1

    if command -v clickhouse-client &>/dev/null; then
        if clickhouse-client --user=default --password=core \
            --query="DROP DATABASE IF EXISTS core" \
            > /dev/null 2>&1; then
            echo "[INFO] ClickHouse database dropped"
        else
            echo "[EROR] ClickHouse drop failed"
            exit 1
        fi
    fi
}

# Migration dependency stub (overridden by distro loaders)
_install_migration_deps() {
    :
}

# Default migration runner (native PG via localhost).
_run_migration() {
    local migration_dir="$1"
    if ! (
        cd "${migration_dir}" && \
        bash migrate.sh \
            "localhost" \
            "core" "core" \
            "core" "core" \
            "5432" \
            "core" \
            "core" "core" \
            "9000"
    ); then
        echo "[EROR] Database migration failed"
        exit 1
    fi
}

# Migration dialog: upgrade from 1.4.x or clean install.
_offer_migration() {
    local unique_vers=()
    for item in "${EVI_FOUND_PACKAGES[@]}"; do
        local v="${item##*=}"
        local already=0
        for u in "${unique_vers[@]+"${unique_vers[@]}"}"; do
            [[ "$u" == "$v" ]] && already=1 && break
        done
        [[ $already -eq 0 ]] && unique_vers+=("$v")
    done

    local found_ver
    if [[ ${#unique_vers[@]} -eq 1 ]]; then
        found_ver="${unique_vers[0]}"
    else
        local sorted
        sorted=$(printf '%s\n' "${unique_vers[@]}" | sort -V)
        local ver_min ver_max
        ver_min=$(echo "$sorted" | head -1)
        ver_max=$(echo "$sorted" | tail -1)
        found_ver="${ver_min} - ${ver_max}"
    fi

    local migration_supported=1
    for v in "${unique_vers[@]}"; do
        if [[ "$v" != 1.4.* ]]; then
            migration_supported=0
            break
        fi
    done

    local has_db_packages=0
    local db_packages=("evi-core")
    for item in "${EVI_FOUND_PACKAGES[@]}"; do
        local pkg_name="${item%%=*}"
        for db_pkg in "${db_packages[@]}"; do
            if [[ "$pkg_name" == "$db_pkg" ]]; then
                has_db_packages=1
                break 2
            fi
        done
    done

    local migration_choice=0
    if [[ $migration_supported -eq 1 ]]; then
        whiptail --title "EVI PLATFORM ${EVI_VERSION} - Migration" \
            --yesno "Found installed EVI version ${found_ver}.\n\nMigrate to ${EVI_VERSION}? (recommended)\n\nYes - backup + run migration script\nNo  - backup + clean install" \
            12 65 || migration_choice=$?
    else
        whiptail --title "EVI PLATFORM ${EVI_VERSION} - Migration" \
            --msgbox "Found installed EVI version ${found_ver}.\n\nMigration is only supported from 1.4.x.\nA clean install will be performed (backup first)." \
            10 65
        migration_choice=1
    fi

    if [[ $migration_choice -eq 255 ]]; then
        echo "[EROR] Migration dialog cancelled."
        exit 0
    fi

    if [[ $migration_choice -eq 0 ]]; then
        if [[ $has_db_packages -eq 1 ]]; then
            _backup_databases
        fi

        _install_migration_deps

        local migration_archive="db_migration-1.4.0-${EVI_VERSION}.tar"
        local migration_dir="${SCRIPT_DIR}/db_migration-1.4.0-${EVI_VERSION}"
        mkdir -p "${migration_dir}"

        echo "[INFO] Downloading migration scripts (${migration_archive})..."
        if ! wget -q --show-progress --no-check-certificate \
                "https://archive.eltex-co.ru/evi-raw/evi-${EVI_VERSION}/${migration_archive}" \
                -O - | tar -xf - -C "${migration_dir}"; then
            echo "[EROR] Failed to download ${migration_archive} from https://archive.eltex-co.ru"
            exit 1
        fi
        echo "[INFO] Migration scripts downloaded to ${migration_dir}"

        echo "[INFO] Running migration 1.4.0 to ${EVI_VERSION}..."
        _run_migration "${migration_dir}"
        echo "[INFO] Database migration completed successfully"
        rm -rf "${migration_dir}"

        touch "${FLAG_MIGRATED}"
        _remove_old_packages
    else
        if [[ $has_db_packages -eq 1 ]]; then
            _backup_databases
            if [[ ! -f "${FLAG_DB_DROPPED}" ]]; then
                if whiptail --title "EVI PLATFORM ${EVI_VERSION} - Databases" \
                    --yesno "Drop EVI databases before clean install?\n\n(A backup has already been created)" \
                    9 60; then
                    _drop_databases
                    touch "${FLAG_DB_DROPPED}"
                fi
            fi
        fi
        _remove_old_packages
    fi
}

########################################################################
# EVI Analytics

_check_nvidia_installed() {
    if nvidia-smi > /dev/null 2>&1; then
        local installed_ver
        installed_ver=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null \
            | head -1 | cut -d'.' -f1)
        echo "[INFO] NVIDIA driver is already installed (version ${installed_ver:-unknown})"
        touch "${FLAG_DRIVER_OK}"
        return 0
    fi
    return 1
}

_remove_analytics_docker() {
    if ! command -v docker &>/dev/null; then
        echo "[WARN] docker not found - skipping analytics container cleanup"
        return 0
    fi

    local found=0

    if docker ps -a --format "{{.Names}}" 2>/dev/null | grep -qx "evi-analytics"; then
        echo "[INFO] Removing old evi-analytics container..."
        docker rm -f evi-analytics > /dev/null 2>&1 || true
        found=1
    fi

    local extra_ids
    extra_ids=$(docker ps -a --format "{{.Image}}\t{{.ID}}" 2>/dev/null \
        | grep -E "hub\.eltex-co\.ru/evi|registry\.eltex\.loc" \
        | awk '{print $2}') || true
    if [[ -n "$extra_ids" ]]; then
        # shellcheck disable=SC2086
        docker rm -f ${extra_ids} > /dev/null 2>&1 || true
        found=1
    fi

    if [[ $found -eq 0 ]]; then
        echo "[WARN] No evi-analytics containers found - skipping"
        return 0
    fi

    docker images --format "{{.Repository}}:{{.Tag}}" 2>/dev/null \
        | grep -E "hub\.eltex-co\.ru/evi|registry\.eltex\.loc" \
        | xargs -r docker rmi -f > /dev/null 2>&1 || true

    echo "[INFO] Old analytics containers and images removed"
}

_wait_evi_services() {
    local services=("evi-core" "evi-scud" "evi-live" "evi-archive" "evi-analyzer")
    systemctl start "${services[@]}" &>/dev/null || true
    echo -n "[INFO] Waiting for EVI services"
    local all_active=false
    local retries=30
    while [[ $retries -gt 0 ]]; do
        all_active=true
        for svc in "${services[@]}"; do
            if ! systemctl is-active --quiet "${svc}"; then
                all_active=false
                break
            fi
        done
        [[ "$all_active" == true ]] && break
        echo -n "."
        sleep 1
        (( retries-- )) || true
    done
    echo ""
    if [[ "$all_active" != true ]]; then
        echo "[WARN] Not all EVI services started within 30s - analytics may not work correctly"
    fi
}

# $@ - KEY=VALUE parameters forwarded to the analytics compose script.
_download_and_run_analytics() {
    local extra_params=("$@")
    local script_name="evi-analytics_${EVI_VERSION}.sh"
    mkdir -p "${DOWNLOAD_DIR}"
    local local_script="${DOWNLOAD_DIR}/${script_name}"

    echo "[INFO] Downloading ${script_name}..."
    if ! curl -fsSL --insecure \
            "https://archive.eltex-co.ru/evi-raw/evi-${EVI_VERSION}/${script_name}" \
            -o "${local_script}"; then
        echo "[EROR] Failed to download ${script_name} from https://archive.eltex-co.ru"
        exit 1
    fi

    chmod +x "${local_script}"
    if ! bash "${local_script}" \
        EVIANALYTICS_POSTGRESQL_HOST="${EVI_LIVE_EXTERN_HOST}" \
        "${extra_params[@]}"; then
        echo "[WARN] Analytics script exited with error (check container logs: docker logs evi-analytics)"
    fi
}

# Default: configure pg_hba + listen_addresses via host files and restart.
# Overridden by _load_rpm (adds bpf_jit_harden) and _load_astra_1_8 (docker exec).
_prepare_pg_for_analytics() {
    _configure_pg_network
    systemctl restart "${PG_SERVICE}" &>/dev/null \
        || { echo "[EROR] Failed to restart ${PG_SERVICE}"; exit 1; }
}

# $@ - extra KEY=VALUE parameters for the compose script.
_run_analytics_compose() {
    local extra_params=("$@")

    local services=("evi-core" "evi-scud" "evi-live" "evi-archive" "evi-analyzer")
    systemctl stop "${services[@]}" &>/dev/null || true

    _prepare_pg_for_analytics
    _wait_evi_services
    _download_and_run_analytics "${extra_params[@]}"
}

install_analytics_gpu() {
    echo "[INFO] Installing EVI Analytics (GPU)..."

    local vram
    vram=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1)

    if [[ -z "$vram" || ! "$vram" =~ ^[0-9]+$ ]]; then
        echo "[EROR] Cannot detect GPU VRAM. Is nvidia-smi working?"
        exit 1
    fi

    if [[ "$vram" -lt 4000 ]]; then
        echo "[EROR] Requires >= 4 GB VRAM. Detected: ${vram} MiB"
        exit 1
    fi
    echo "[INFO] VRAM: ${vram} MiB - OK"

    _install_docker "gpu"

    _run_analytics_compose \
        EVIANALYTICS_HWACCEL_VIDEO_DECODER="gpu"

    rm -f "${FLAG_REBOOT_NEEDED}" "${FLAG_DRIVER_OK}" "${FLAG_ANALYTICS_TYPE}" "${FLAG_REBOOTED}"
}

install_analytics_cpu() {
    echo "[INFO] Installing EVI Analytics (CPU)..."
    _install_docker "cpu"
    _run_analytics_compose \
        EVIANALYTICS_HWACCEL_VIDEO_DECODER="cpu" \
        EVIANALYTICS_NN_USE_CPU="true"
}

########################################################################
# Distro: DEB platform (apt/dpkg)

_load_deb() {
    PG_SERVICE="${PG_SERVICE:-postgresql}"
    PG_HBA="${PG_HBA:-/etc/postgresql/17/main/pg_hba.conf}"
    PG_CONF="${PG_CONF:-/etc/postgresql/17/main/postgresql.conf}"
    PKG_EXT="deb"

    _pkg_install() {
        DEBIAN_FRONTEND=noninteractive apt install -y -qqqq \
            -o Dpkg::Options::="--force-confold" "$@" > /dev/null 2>&1
    }
    _pkg_remove() {
        DEBIAN_FRONTEND=noninteractive apt -y -qqq purge "$@" > /dev/null 2>&1
    }
    _pkg_autoremove() {
        DEBIAN_FRONTEND=noninteractive apt -y -qqq autoremove > /dev/null 2>&1
    }
    _pkg_is_installed() { dpkg -s "$1" &>/dev/null; }
    _pkg_get_version() {
        dpkg -s "$1" 2>/dev/null | awk '/^Version:/{print $2}' | cut -d'-' -f1
    }

    _install_migration_deps() {
        echo -n "[INFO] Installing migration dependencies"
        {
            DEBIAN_FRONTEND=noninteractive apt-get install -y -qqqq \
                -o Dpkg::Options::="--force-confold" \
                python3 python3-pip > /dev/null 2>&1
            pip3 install python-dateutil > /dev/null 2>&1
        } &
        _wait_bg
        echo "[INFO] Migration dependencies installed"
    }

    _setup_repos() {
        echo "[INFO] Installing prerequisites..."
        if ! DEBIAN_FRONTEND=noninteractive apt install -y -qqqq \
            -o Dpkg::Options::="--force-confold" \
            ca-certificates apt-transport-https gnupg curl wget lsb-release > /dev/null 2>&1; then
            echo "[EROR] Failed to install prerequisite packages"
            exit 1
        fi

        if [[ ! -f /etc/apt/sources.list.d/pgdg.list ]]; then
            echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
                > /etc/apt/sources.list.d/pgdg.list
            if ! wget -t 3 --timeout=10 --waitretry=2 -qO - \
                    https://www.postgresql.org/media/keys/ACCC4CF8.asc \
                | gpg --yes --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg; then
                echo "[EROR] Failed to connect to PostgreSQL servers"
                exit 1
            fi
        fi

        if [[ ! -f /etc/apt/sources.list.d/clickhouse.list ]]; then
            if ! curl --retry 10 --retry-delay 0 --connect-timeout 2 --max-time 5 -fsSL \
                    'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' \
                | gpg --yes --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg \
                > /dev/null 2>&1; then
                echo "[EROR] Failed to connect to ClickHouse servers"
                exit 1
            fi
            local arch
            arch=$(dpkg --print-architecture)
            echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg arch=${arch}] \
https://packages.clickhouse.com/deb stable main" \
                | tee /etc/apt/sources.list.d/clickhouse.list > /dev/null
        fi

        if [[ ! -f /etc/apt/sources.list.d/angie.list ]]; then
            if ! wget -qO /etc/apt/trusted.gpg.d/angie-signing.gpg \
                    https://angie.software/keys/angie-signing.gpg; then
                echo "[EROR] Failed to download Angie GPG key"
                exit 1
            fi
            echo "deb https://download.angie.software/angie/${ID}/${VERSION_ID} ${VERSION_CODENAME} main" \
                | tee /etc/apt/sources.list.d/angie.list > /dev/null
        fi

        echo "[INFO] Updating package lists..."
        if ! apt update -y -qqqq; then
            echo "[WARN] Package list update finished with errors (non-critical)"
        fi
        echo "[INFO] Repositories configured"
    }

    _install_postgres() {
        echo "[INFO] Installing PostgreSQL 17..."
        {
            DEBIAN_FRONTEND=noninteractive apt install -y -qqqq \
                -o Dpkg::Options::="--force-confold" postgresql-17 > /dev/null 2>&1
        } &
        _wait_bg

        if dpkg -s "postgresql-17" &>/dev/null; then
            echo "[INFO] PostgreSQL 17 successfully installed"
        else
            echo "[EROR] PostgreSQL 17 installation error"
            exit 1
        fi

        systemctl enable "${PG_SERVICE}" > /dev/null 2>&1 || true
        systemctl start "${PG_SERVICE}" > /dev/null 2>&1 \
            || { echo "[EROR] Failed to start ${PG_SERVICE}"; exit 1; }

        _configure_pg_core_db
        _configure_pg_network

        systemctl restart "${PG_SERVICE}" > /dev/null 2>&1 \
            || { echo "[EROR] Failed to restart ${PG_SERVICE}"; exit 1; }
        echo "[INFO] PostgreSQL configured"
        echo ""
    }

    _install_clickhouse() {
        echo "clickhouse-server clickhouse-server/password password core" | debconf-set-selections || true
        echo "clickhouse-server clickhouse-server/password_again password core" | debconf-set-selections || true

        echo -n "[INFO] Installing ClickHouse server"
        {
            DEBIAN_FRONTEND=noninteractive apt install -y -qqqq \
                -o Dpkg::Options::="--force-confold" clickhouse-server > /dev/null 2>&1
        } &
        _wait_bg

        if dpkg -s "clickhouse-server" &>/dev/null; then
            echo "[INFO] clickhouse-server successfully installed"
        else
            echo "[EROR] clickhouse-server installation error"
            exit 1
        fi

        echo -n "[INFO] Installing ClickHouse client"
        {
            DEBIAN_FRONTEND=noninteractive apt install -y -qqqq \
                -o Dpkg::Options::="--force-confold" clickhouse-client > /dev/null 2>&1
        } &
        _wait_bg

        if dpkg -s "clickhouse-client" &>/dev/null; then
            echo "[INFO] clickhouse-client successfully installed"
        else
            echo "[EROR] clickhouse-client installation error"
            exit 1
        fi

        _configure_clickhouse
    }

    _install_angie() {
        echo "[INFO] Installing Angie..."
        {
            DEBIAN_FRONTEND=noninteractive apt install -y -qqqq \
                -o Dpkg::Options::="--force-confold" angie > /dev/null 2>&1
        } &
        _wait_bg

        if dpkg -s "angie" &>/dev/null; then
            echo "[INFO] Angie successfully installed"
        else
            echo "[EROR] Angie installation error"
            exit 1
        fi

        systemctl enable angie > /dev/null 2>&1 || true
        systemctl start angie > /dev/null 2>&1 \
            || { echo "[EROR] Failed to start angie"; exit 1; }
    }

    _install_web_server() { _install_angie; }
    _post_install_web_server() {
        systemctl restart angie > /dev/null 2>&1 || true
        echo "[INFO] Angie restarted after evi-core-web install"
    }

    _install_nvidia_driver() {
        local nvidia_driver_version="${NVIDIA_DRIVER_VERSION:-580}"
        local nvidia_pkg="nvidia-driver-${nvidia_driver_version}-open"

        echo "[INFO] Checking NVIDIA driver..."

        if _check_nvidia_installed; then
            return 0
        fi

        echo -n "[INFO] Installing ${nvidia_pkg}"
        {
            DEBIAN_FRONTEND=noninteractive apt install -y -qqqq \
                -o Dpkg::Options::="--force-confold" "${nvidia_pkg}" > /dev/null 2>&1
        } &
        _wait_bg

        if ! dpkg -s "${nvidia_pkg}" &>/dev/null; then
            echo "[EROR] ${nvidia_pkg} installation error"
            exit 1
        fi
        echo "[INFO] NVIDIA driver ${nvidia_driver_version} installed"

        touch "${FLAG_REBOOT_NEEDED}"
    }

    # $1 - "gpu" or "cpu"
    _install_docker() {
        local mode="${1:-cpu}"

        if command -v docker &>/dev/null; then
            echo "[INFO] Docker already installed"
        else
            echo -n "[INFO] Installing Docker"
            {
                curl -fsSL https://get.docker.com | sh > /dev/null 2>&1
            } &
            _wait_bg

            if ! command -v docker &>/dev/null; then
                echo "[EROR] Docker installation error"
                exit 1
            fi
            systemctl enable docker > /dev/null 2>&1 || true
            systemctl start docker > /dev/null 2>&1 \
                || { echo "[EROR] Failed to start docker"; exit 1; }
            echo "[INFO] Docker installed"
        fi

        if [[ "${mode}" == "gpu" ]]; then
            echo -n "[INFO] Installing NVIDIA Container Toolkit"
            {
                _nct_distrib=$(. /etc/os-release; echo "${ID}${VERSION_ID}")
                curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
                    | gpg --yes --dearmor \
                        -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg 2>/dev/null
                curl -sL \
                    "https://nvidia.github.io/libnvidia-container/${_nct_distrib}/libnvidia-container.list" \
                    | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
                    | tee /etc/apt/sources.list.d/nvidia-container-toolkit.list > /dev/null
                apt-get update -qq > /dev/null 2>&1
                DEBIAN_FRONTEND=noninteractive apt-get install -y -qqqq \
                    -o Dpkg::Options::="--force-confold" nvidia-docker2 > /dev/null 2>&1
            } &
            _wait_bg

            if ! dpkg -s "nvidia-docker2" &>/dev/null; then
                echo "[EROR] nvidia-docker2 installation error"
                exit 1
            fi
            systemctl restart docker > /dev/null 2>&1 \
                || { echo "[EROR] Failed to restart docker after nvidia-docker2 install"; exit 1; }
            echo "[INFO] NVIDIA Container Toolkit installed"
        fi

        if [[ -n "${SUDO_USER:-}" ]]; then
            if usermod -aG docker "${SUDO_USER}"; then
                echo "[INFO] User '${SUDO_USER}' added to docker group"
            else
                echo "[WARN] Failed to add '${SUDO_USER}' to docker group"
            fi
        fi
    }
}

########################################################################
# Distro: RPM platform (dnf/rpm)

_load_rpm() {
    PG_SERVICE="${PG_SERVICE:-postgresql-17}"
    PG_HBA="${PG_HBA:-/var/lib/pgsql/17/data/pg_hba.conf}"
    PG_CONF="${PG_CONF:-/var/lib/pgsql/17/data/postgresql.conf}"
    PG_INITDB="${PG_INITDB:-postgresql-17-setup initdb}"
    PKG_EXT="rpm"

    _pkg_install() {
        dnf install -y -qqqq "$@" > /dev/null 2>&1
    }
    _pkg_remove() {
        dnf -y -q remove "$@" > /dev/null 2>&1
    }
    _pkg_autoremove() {
        dnf -y -q autoremove > /dev/null 2>&1
    }
    _pkg_is_installed() { rpm -q "$1" &>/dev/null; }
    _pkg_get_version() {
        local v
        v=$(rpm -q --queryformat '%{VERSION}' "$1" 2>/dev/null)
        echo "${v%%_*}"
    }

    _install_migration_deps() {
        echo -n "[INFO] Installing migration dependencies"
        {
            dnf install -y -qqqq python3 python3-pip > /dev/null 2>&1
            pip3 install python-dateutil > /dev/null 2>&1
        } &
        _wait_bg
        echo "[INFO] Migration dependencies installed"
    }

    _setup_repos() {
        echo "[INFO] Configuring ClickHouse repository..."
        cat > /etc/yum.repos.d/clickhouse.repo <<EOF
[clickhouse]
name=ClickHouse Repository
baseurl=${CLICKHOUSE_REPO_BASEURL}
enabled=1
gpgcheck=0
EOF

        echo "[INFO] Updating package lists..."
        if ! dnf update -y -qqqq > /dev/null 2>&1; then
            echo "[WARN] Package list update finished with errors (non-critical)"
        fi
        echo "[INFO] Repositories configured"
    }

    _install_postgres() {
        echo "[INFO] Installing PostgreSQL 17..."
        {
            dnf install -y -qqqq postgresql17-server > /dev/null 2>&1
        } &
        _wait_bg

        if ! rpm -q postgresql17-server &>/dev/null; then
            echo "[EROR] PostgreSQL 17 (postgresql17-server) installation error"
            exit 1
        fi
        echo "[INFO] postgresql17-server installed"

        echo -n "[INFO] Installing postgresql17-contrib"
        {
            dnf install -y -qqqq postgresql17-contrib > /dev/null 2>&1
        } &
        _wait_bg

        if ! rpm -q postgresql17-contrib &>/dev/null; then
            echo "[EROR] postgresql17-contrib installation error"
            exit 1
        fi
        echo "[INFO] postgresql17-contrib installed"

        local pg_data_dir
        pg_data_dir=$(dirname "${PG_HBA}")
        if [[ ! -f "${pg_data_dir}/PG_VERSION" ]]; then
            echo "[INFO] Initializing PostgreSQL cluster..."
            ${PG_INITDB} > /dev/null 2>&1 \
                || { echo "[EROR] PostgreSQL cluster initialization failed"; exit 1; }
        fi

        systemctl enable "${PG_SERVICE}" > /dev/null 2>&1 || true
        systemctl start "${PG_SERVICE}" > /dev/null 2>&1 \
            || { echo "[EROR] Failed to start ${PG_SERVICE}"; exit 1; }

        _configure_pg_core_db
        _configure_pg_network

        systemctl restart "${PG_SERVICE}" > /dev/null 2>&1 \
            || { echo "[EROR] Failed to restart ${PG_SERVICE}"; exit 1; }
        echo "[INFO] PostgreSQL configured"
        echo ""
    }

    _install_clickhouse() {
        echo -n "[INFO] Installing clickhouse-common-static-${CLICKHOUSE_VERSION}"
        {
            dnf install -y -qqqq "clickhouse-common-static-${CLICKHOUSE_VERSION}" > /dev/null 2>&1
        } &
        _wait_bg

        if ! rpm -q clickhouse-common-static &>/dev/null; then
            echo "[EROR] clickhouse-common-static installation error"
            exit 1
        fi
        echo "[INFO] clickhouse-common-static installed"

        echo -n "[INFO] Installing clickhouse-server-${CLICKHOUSE_VERSION}"
        {
            dnf install -y -qqqq "clickhouse-server-${CLICKHOUSE_VERSION}" > /dev/null 2>&1
        } &
        _wait_bg

        if ! rpm -q clickhouse-server &>/dev/null; then
            echo "[EROR] clickhouse-server installation error"
            exit 1
        fi
        echo "[INFO] clickhouse-server installed"

        echo -n "[INFO] Installing clickhouse-client-${CLICKHOUSE_VERSION}"
        {
            dnf install -y -qqqq "clickhouse-client-${CLICKHOUSE_VERSION}" > /dev/null 2>&1
        } &
        _wait_bg

        if ! rpm -q clickhouse-client &>/dev/null; then
            echo "[EROR] clickhouse-client installation error"
            exit 1
        fi
        echo "[INFO] clickhouse-client installed"

        _configure_clickhouse
    }

    _install_web_server() { :; }
    _post_install_web_server() {
        systemctl enable angie > /dev/null 2>&1 || true
        systemctl restart angie > /dev/null 2>&1 || true
        echo "[INFO] Angie enabled and started after evi-core-web install"
    }

    _install_nvidia_driver() {
        local nvidia_driver_version="${NVIDIA_DRIVER_VERSION:-580}"

        echo "[INFO] Checking NVIDIA driver..."

        if _check_nvidia_installed; then
            return 0
        fi

        echo -n "[INFO] Installing nvidia-drivers (required >= ${nvidia_driver_version})"
        {
            dnf install -y -qqqq nvidia-drivers > /dev/null 2>&1
        } &
        _wait_bg

        if ! rpm -q nvidia-drivers &>/dev/null; then
            echo "[EROR] nvidia-drivers installation error"
            exit 1
        fi
        echo "[INFO] NVIDIA driver installed"

        touch "${FLAG_REBOOT_NEEDED}"
    }

    # $1 - "gpu" or "cpu"
    _install_docker() {
        local mode="${1:-cpu}"

        if [[ "${mode}" == "gpu" ]] && [[ ! -f /etc/yum.repos.d/nvidia-container-toolkit.repo ]]; then
            echo "[INFO] Configuring NVIDIA Container Toolkit repository..."
            curl -fsSL https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo \
                | tee /etc/yum.repos.d/nvidia-container-toolkit.repo > /dev/null 2>&1
        fi

        if command -v docker &>/dev/null; then
            echo "[INFO] Docker already installed"
        else
            echo -n "[INFO] Installing Docker CE"
            {
                dnf install -y -qqqq docker-ce > /dev/null 2>&1
                dnf install -y -qqqq docker-compose > /dev/null 2>&1
            } &
            _wait_bg

            if ! command -v docker &>/dev/null; then
                echo "[EROR] Docker installation error"
                exit 1
            fi
            systemctl enable docker > /dev/null 2>&1 || true
            systemctl start docker > /dev/null 2>&1 \
                || { echo "[EROR] Failed to start docker"; exit 1; }

            local retries=30
            while ! systemctl is-active --quiet docker.service; do
                sleep 1
                retries=$(( retries - 1 ))
                if [[ $retries -le 0 ]]; then
                    echo "[EROR] Docker service failed to start"
                    exit 1
                fi
            done
            echo "[INFO] Docker installed"
        fi

        if [[ "${mode}" == "gpu" ]]; then
            echo -n "[INFO] Installing NVIDIA Container Toolkit"
            {
                dnf install -y -qqqq nvidia-container-toolkit > /dev/null 2>&1
            } &
            _wait_bg

            if ! rpm -q nvidia-container-toolkit &>/dev/null; then
                echo "[EROR] nvidia-container-toolkit installation error"
                exit 1
            fi

            if ! nvidia-ctk runtime configure --runtime=docker > /dev/null 2>&1; then
                echo "[WARN] nvidia-ctk runtime configure failed"
            fi
            systemctl restart docker > /dev/null 2>&1 \
                || { echo "[EROR] Failed to restart docker after nvidia-ctk configure"; exit 1; }
            echo "[INFO] NVIDIA Container Toolkit installed"
        fi

        if [[ -n "${SUDO_USER:-}" ]]; then
            if usermod -aG docker "${SUDO_USER}"; then
                echo "[INFO] User '${SUDO_USER}' added to docker group"
            else
                echo "[WARN] Failed to add '${SUDO_USER}' to docker group"
            fi
        fi
    }

    # RPM-specific: add bpf_jit_harden before PG network config.
    _prepare_pg_for_analytics() {
        sysctl -w net.core.bpf_jit_harden=1 > /dev/null 2>&1 || true
        _configure_pg_network
        systemctl restart "${PG_SERVICE}" > /dev/null 2>&1 \
            || { echo "[EROR] Failed to restart ${PG_SERVICE}"; exit 1; }
    }
}

########################################################################
# Distro: Ubuntu 22.04

_load_ubuntu_22_04() {
    EVI_TARBALL="evi-${EVI_VERSION}_ubuntu-22.04.tar"
    _load_deb
}

########################################################################
# Distro: RedOS

_load_redos() {
    case "$1" in
        7.3)
            CLICKHOUSE_VERSION="25.1.4.53"
            CLICKHOUSE_REPO_BASEURL="https://mirror.yandex.ru/redos/7.3/x86_64/3rdparty/clickhouse/"
            ;;
        8.0)
            CLICKHOUSE_VERSION="25.9.4.58"
            CLICKHOUSE_REPO_BASEURL="https://mirror.yandex.ru/redos/8.0/x86_64/3rdparty/clickhouse/"
            ;;
    esac
    EVI_TARBALL="evi-${EVI_VERSION}_redos.tar"
    _load_rpm
}

########################################################################
# Distro: Astra Linux 1.8

_load_astra_1_8() {
    EVI_TARBALL="evi-${EVI_VERSION}_ubuntu-22.04.tar"
    _load_deb

    # PostgreSQL adapter overrides (Docker PG)
    _pg_dump_cmd()   { docker exec postgresql pg_dump -U core "$@"; }
    _pg_admin_psql() { docker exec postgresql psql -U core "$@"; }

    # Drop + recreate core database (Docker PG requires explicit recreate).
    _pg_drop_core() {
        if docker exec postgresql psql -U core --quiet \
            -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='core' AND pid <> pg_backend_pid();" \
            -c "DROP DATABASE IF EXISTS core;" \
            -c "DROP ROLE IF EXISTS core;" \
            > /dev/null 2>&1; then
            echo "[INFO] PostgreSQL databases dropped"
            docker exec postgresql psql -U postgres --quiet \
                -c "CREATE DATABASE core;" \
                -c "CREATE ROLE core LOGIN PASSWORD 'core';
                    GRANT ALL ON DATABASE core TO core;
                    ALTER DATABASE core OWNER TO core;" \
                > /dev/null 2>&1
            echo "[INFO] PostgreSQL database and role recreated"
        else
            echo "[EROR] PostgreSQL drop failed"
            return 1
        fi
    }

    _astra_enable_net_repos() {
        local list="/etc/apt/sources.list.d/astra-repo.list"

        if [[ -f "${list}" ]]; then
            return 0
        fi

        if grep -qs "^deb .*1.8_x86-64/repository-main/" /etc/apt/sources.list 2>/dev/null \
           && grep -qs "^deb .*1.8_x86-64/repository-extended/" /etc/apt/sources.list 2>/dev/null; then
            echo "[INFO] Astra repository-main and repository-extended already configured in sources.list"
            return 0
        fi

        cat > "${list}" <<'EOF'
deb https://download.astralinux.ru/astra/stable/1.8_x86-64/repository-main/ 1.8_x86-64 main contrib non-free non-free-firmware
deb https://download.astralinux.ru/astra/stable/1.8_x86-64/repository-extended/ 1.8_x86-64 main contrib non-free non-free-firmware
EOF
    }

    # No pgdg (PG is Docker), ClickHouse + Angie (astra-specific URL) + Astra repos.
    _setup_repos() {
        echo "[INFO] Installing prerequisites..."
        if ! DEBIAN_FRONTEND=noninteractive apt-get install -y -qqqq \
            -o Dpkg::Options::="--force-confold" \
            ca-certificates apt-transport-https gnupg curl wget lsb-release > /dev/null 2>&1; then
            echo "[EROR] Failed to install prerequisite packages"
            exit 1
        fi

        if ! curl --retry 10 --retry-delay 0 --connect-timeout 2 --max-time 5 -fsSL \
                'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' \
            | gpg --yes --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg \
            > /dev/null 2>&1; then
            echo "[EROR] Failed to connect to ClickHouse servers"
            exit 1
        fi
        local arch
        arch=$(dpkg --print-architecture)
        echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg arch=${arch}] \
https://packages.clickhouse.com/deb stable main" \
            | tee /etc/apt/sources.list.d/clickhouse.list > /dev/null

        if ! wget -qO /etc/apt/trusted.gpg.d/angie-signing.gpg \
                https://angie.software/keys/angie-signing.gpg; then
            echo "[EROR] Failed to download Angie GPG key"
            exit 1
        fi
        echo "deb https://download.angie.software/angie/astra-se/1.8 unstable main" \
            | tee /etc/apt/sources.list.d/angie.list > /dev/null

        _astra_enable_net_repos

        echo "[INFO] Updating package lists..."
        if ! apt-get update -qqqq; then
            echo "[WARN] Package list update finished with errors (non-critical)"
        fi
        echo "[INFO] Repositories configured"
    }

    _astra_install_docker() {
        if dpkg -s "docker-compose-v2" &>/dev/null; then
            echo "[INFO] docker-compose-v2 already installed"
            return 0
        fi

        _astra_enable_net_repos
        if ! apt-get update -qqqq > /dev/null 2>&1; then
            echo "[WARN] Package list update finished with errors (non-critical)"
        fi

        echo -n "[INFO] Installing docker-compose-v2"
        {
            DEBIAN_FRONTEND=noninteractive apt-get install -y -qqqq \
                -o Dpkg::Options::="--force-confold" \
                docker-compose-v2 > /dev/null 2>&1
        } &
        _wait_bg

        if ! dpkg -s "docker-compose-v2" &>/dev/null; then
            echo "[EROR] docker-compose-v2 installation error"
            exit 1
        fi
        if ! command -v docker &>/dev/null; then
            echo "[EROR] docker not available after docker-compose-v2 install"
            exit 1
        fi

        systemctl enable docker > /dev/null 2>&1 || true
        systemctl start docker > /dev/null 2>&1 \
            || { echo "[EROR] Failed to start docker"; exit 1; }
        echo "[INFO] docker-compose-v2 installed"
    }

    # Migration is not supported on Astra Linux (PostgreSQL runs in Docker).
    _offer_migration() {
        echo "[EROR] Found previously installed EVI packages."
        echo "[EROR] Migration is not supported on Astra Linux (PostgreSQL runs in Docker)."
        echo "[EROR] Please perform a clean install on a fresh system."
        exit 1
    }

    _install_nvidia_driver() {
        local nvidia_driver_version="${NVIDIA_DRIVER_VERSION:-580}"
        local nvidia_pkg="nvidia-driver-${nvidia_driver_version}-open"

        echo "[INFO] Checking NVIDIA driver..."

        if _check_nvidia_installed; then
            return 0
        fi

        _astra_enable_net_repos
        if ! apt-get update -qqqq > /dev/null 2>&1; then
            echo "[WARN] Package list update finished with errors (non-critical)"
        fi

        echo -n "[INFO] Installing ${nvidia_pkg}"
        {
            DEBIAN_FRONTEND=noninteractive apt install -y -qqqq \
                -o Dpkg::Options::="--force-confold" "${nvidia_pkg}" > /dev/null 2>&1
        } &
        _wait_bg

        if ! dpkg -s "${nvidia_pkg}" &>/dev/null; then
            echo "[EROR] ${nvidia_pkg} installation error"
            exit 1
        fi
        echo "[INFO] NVIDIA driver ${nvidia_driver_version} installed"

        touch "${FLAG_REBOOT_NEEDED}"
    }

    # docker-compose-v2 + optional NVIDIA toolkit.
    _install_docker() {
        local mode="${1:-cpu}"

        _astra_install_docker

        if [[ "${mode}" == "gpu" ]]; then
            local nct_version="${NVIDIA_CONTAINER_TOOLKIT_VERSION:-1.19.0-1}"

            echo -n "[INFO] Installing NVIDIA Container Toolkit (${nct_version})"
            {
                curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
                    | gpg --yes --dearmor \
                        -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg 2>/dev/null
                curl -sL \
                    "https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list" \
                    | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
                    | tee /etc/apt/sources.list.d/nvidia-container-toolkit.list > /dev/null
                sed -i -e '/experimental/ s/^#//g' \
                    /etc/apt/sources.list.d/nvidia-container-toolkit.list
                if ! apt-get update -qq > /dev/null 2>&1; then
                    echo "[WARN] Package list update finished with errors (non-critical)"
                fi
                DEBIAN_FRONTEND=noninteractive apt-get install -y -qqqq \
                    -o Dpkg::Options::="--force-confold" \
                    "nvidia-container-toolkit=${nct_version}" \
                    "nvidia-container-toolkit-base=${nct_version}" \
                    "libnvidia-container-tools=${nct_version}" \
                    "libnvidia-container1=${nct_version}" \
                    > /dev/null 2>&1
            } &
            _wait_bg

            if ! dpkg -s "nvidia-container-toolkit" &>/dev/null; then
                echo "[EROR] nvidia-container-toolkit installation error"
                exit 1
            fi

            if ! nvidia-ctk runtime configure --runtime=docker > /dev/null 2>&1; then
                echo "[WARN] nvidia-ctk runtime configure failed"
            fi
            systemctl restart docker > /dev/null 2>&1 \
                || { echo "[EROR] Failed to restart docker after nvidia-ctk configure"; exit 1; }
            echo "[INFO] NVIDIA Container Toolkit installed"
        fi

        if [[ -n "${SUDO_USER:-}" ]]; then
            if usermod -aG docker "${SUDO_USER}"; then
                echo "[INFO] User '${SUDO_USER}' added to docker group"
            else
                echo "[WARN] Failed to add '${SUDO_USER}' to docker group"
            fi
        fi
    }

    # PostgreSQL 17 in Docker.
    _install_postgres() {
        echo "[INFO] Installing PostgreSQL 17 (Docker)..."

        _astra_install_docker

        systemctl stop evi-archive evi-analyzer evi-live evi-scud evi-core &>/dev/null || true

        if docker ps -a --format "{{.Names}}" 2>/dev/null | grep -qx "postgresql"; then
            echo "[INFO] PostgreSQL container already exists - skipping creation"
        else
            docker volume create postgres_data > /dev/null 2>&1

            echo -n "[INFO] Starting PostgreSQL 17 container"
            {
                docker run -d \
                    -e POSTGRES_PASSWORD=core \
                    -e POSTGRES_USER=core \
                    -e POSTGRES_DB=core \
                    -p 5432:5432 \
                    --name postgresql \
                    --restart always \
                    -v postgres_data:/var/lib/postgresql/data \
                    postgres:17 > /dev/null 2>&1
            } &
            _wait_bg

            if ! docker ps --format "{{.Names}}" 2>/dev/null | grep -qx "postgresql"; then
                echo "[EROR] PostgreSQL container failed to start"
                exit 1
            fi
            echo "[INFO] PostgreSQL container started"
        fi

        echo -n "[INFO] Waiting for PostgreSQL"
        local retries=60
        while ! docker exec postgresql psql -U core -c "SELECT 1" > /dev/null 2>&1; do
            echo -n "."
            sleep 1
            retries=$(( retries - 1 ))
            if [[ $retries -le 0 ]]; then
                echo ""
                echo "[EROR] PostgreSQL not ready after 60s"
                exit 1
            fi
        done
        echo ""

        echo "[INFO] Configuring PostgreSQL..."
        if ! docker exec postgresql psql -U core --quiet \
            -c "ALTER USER core WITH PASSWORD 'core';
                GRANT ALL ON DATABASE core TO core;
                ALTER DATABASE core OWNER TO core;
                GRANT ALL PRIVILEGES ON DATABASE core TO core;
                ALTER ROLE core WITH LOGIN;" \
            > /dev/null 2>&1; then
            echo "[EROR] Failed to configure PostgreSQL database in Docker"
            exit 1
        fi
        echo "[INFO] PostgreSQL database configured"

        if ! docker exec postgresql grep -q "172.16.0.0/12" \
                /var/lib/postgresql/data/pg_hba.conf 2>/dev/null; then
            docker exec postgresql bash -c \
                "echo 'host all all 172.16.0.0/12 md5' >> /var/lib/postgresql/data/pg_hba.conf"
            docker exec postgresql psql -U core \
                -c "SELECT pg_reload_conf();" > /dev/null 2>&1
        fi

        echo "[INFO] PostgreSQL configured"
        echo ""
    }

    # Docker PG: modify pg_hba inside container.
    _prepare_pg_for_analytics() {
        if ! docker exec postgresql grep -q "172.16.0.0/12" \
                /var/lib/postgresql/data/pg_hba.conf 2>/dev/null; then
            docker exec postgresql bash -c \
                "echo 'host all all 172.16.0.0/12 md5' >> /var/lib/postgresql/data/pg_hba.conf"
            docker exec postgresql psql -U core \
                -c "SELECT pg_reload_conf();" > /dev/null 2>&1
        fi
    }
}

########################################################################
# OS detection and preparation

prepare() {
    mkdir -p "${STATE_DIR}"

    set +u
    if [[ -z "${EVI_LIVE_EXTERN_HOST}" ]]; then
        EVI_LIVE_EXTERN_HOST=$(_detect_extern_host)
        export EVI_LIVE_EXTERN_HOST
    fi
    set -u

    echo "[INFO] External host: ${EVI_LIVE_EXTERN_HOST}"

    if ! grep -w -q "avx2" /proc/cpuinfo; then
        echo "[EROR] Your processor does not support AVX2. Cancel installation."
        exit 1
    fi

    if [ ! -f /etc/os-release ]; then
        echo "[EROR] OS detection failed: /etc/os-release not found."
        exit 1
    fi

    # shellcheck source=/dev/null
    source /etc/os-release

    case "$ID" in
        ubuntu)
            if [ "$VERSION_ID" != "22.04" ]; then
                echo "[EROR] Installation on Ubuntu $VERSION_ID is not supported."
                echo "[INFO] Only Ubuntu 22.04 is supported."
                exit 1
            fi
            OS="UBUNTU22"
            _load_ubuntu_22_04
            ;;
        redos)
            REDOS_VERSION=$(echo "$VERSION_ID" | grep -oP '^\d+\.\d+')
            OS="REDOS_${REDOS_VERSION//./_}"
            case "$REDOS_VERSION" in
                7.3|8.0) _load_redos "$REDOS_VERSION" ;;
                *)
                    echo "[EROR] RedOS $VERSION_ID is not supported. Supported: 7.3, 8.0"
                    exit 1
                    ;;
            esac
            ;;
        astra)
            ASTRA_VERSION=$(grep -oP '[0-9]+\.[0-9]+' /etc/astra_version 2>/dev/null || echo "$VERSION_ID")
            OS="ASTRA_${ASTRA_VERSION//./_}"
            case "$ASTRA_VERSION" in
                1.8) _load_astra_1_8 ;;
                *)
                    echo "[EROR] Astra Linux $ASTRA_VERSION is not supported. Supported: 1.8"
                    exit 1
                    ;;
            esac
            ;;
        *)
            echo "[EROR] Installation on '$PRETTY_NAME' is not supported."
            exit 1
            ;;
    esac

    export OS

    _ensure_whiptail || exit 1

    echo "[INFO] Detected OS: $PRETTY_NAME → profile: $OS (${VERSION_ID})"
}

########################################################################
# Installation orchestrator

_check_evi_installed() {
    if [[ -f "${FLAG_REBOOT_NEEDED}" ]]; then
        return 0
    fi

    if [[ -f "${FLAG_MIGRATED}" ]]; then
        return 0
    fi

    EVI_FOUND_PACKAGES=()
    EVI_CURRENT_PACKAGES=()
    EVI_ANALYTICS_DOCKER_FOUND=0
    EVI_ANALYTICS_DOCKER_CURRENT=0

    _detect_evi_packages

    if [[ ${#EVI_FOUND_PACKAGES[@]} -eq 0 ]]; then
        if [[ ${#EVI_CURRENT_PACKAGES[@]} -gt 0 ]] || [[ "${EVI_ANALYTICS_DOCKER_CURRENT}" -eq 1 ]]; then
            echo "[WARN] EVI ${EVI_VERSION} is already installed:"
            for item in "${EVI_CURRENT_PACKAGES[@]}"; do
                echo "[INFO] - ${item%%=*}"
            done
            if [[ "${EVI_ANALYTICS_DOCKER_CURRENT}" -eq 1 ]]; then
                echo "[INFO] - evi-analytics (Docker)"
            fi
            echo "[WARN] Proceeding with reinstall / component addition."
        else
            echo "[INFO] No previously installed EVI packages found"
        fi
        if [[ "${EVI_ANALYTICS_DOCKER_FOUND}" -eq 1 ]]; then
            if whiptail --title "EVI PLATFORM ${EVI_VERSION}" \
                --yesno "Found old evi-analytics Docker container.\n\nRemove it before installing?" \
                9 60; then
                _remove_analytics_docker
            fi
        fi
        return 0
    fi

    echo "[INFO] Found installed EVI packages (older than ${EVI_VERSION}):"
    for item in "${EVI_FOUND_PACKAGES[@]}"; do
        echo "[INFO] - ${item/=/ v}"
    done

    _offer_migration
}

# $1 - space-separated string of selected component names.
install_selected() {
    local res="$1"
    local sel=" ${res} "

    local pkg_names=("evi-core" "evi-core-web" "evi-scud" "evi-live" "evi-analyzer" "evi-archive" "evi-video-client")
    local need_pkgs=0
    for pkg in "${pkg_names[@]}"; do
        [[ "$sel" == *" ${pkg} "* ]] && need_pkgs=1 && break
    done
    if [[ $need_pkgs -eq 1 ]]; then
        echo "[INFO] >>> [1/5] Downloading EVI ${EVI_VERSION} packages..."
        _download_evi_packages
    fi

    if [[ "$sel" == *" evi-core "* ]]; then
        echo "[INFO] >>> [2/5] Configuring infrastructure (PostgreSQL, ClickHouse)..."
        _setup_repos
        _install_postgres
        _install_clickhouse
    fi

    if [[ "$sel" == *" evi-core-web "* ]]; then
        _install_web_server
    fi

    local evi_pkgs=("evi-core" "evi-core-web" "evi-scud" "evi-live" "evi-analyzer" "evi-archive" "evi-video-client")
    local failed_pkgs=()
    if [[ $need_pkgs -eq 1 ]]; then
        echo "[INFO] >>> [3/5] Installing EVI packages..."
        for pkg in "${evi_pkgs[@]}"; do
            if [[ "$sel" == *" ${pkg} "* ]]; then
                _install_package "${pkg}" || failed_pkgs+=("${pkg}")
            fi
        done
    fi

    if [[ "$sel" == *" evi-core-web "* ]] && ! [[ " ${failed_pkgs[*]:-} " == *" evi-core-web "* ]]; then
        _post_install_web_server
    fi

    local svc_pkgs=("evi-core" "evi-scud" "evi-live" "evi-analyzer" "evi-archive")
    local started_services=()
    for svc in "${svc_pkgs[@]}"; do
        if [[ "$sel" == *" ${svc} "* ]]; then
            if [[ " ${failed_pkgs[*]:-} " == *" ${svc} "* ]]; then
                continue
            fi
            systemctl enable "${svc}" > /dev/null 2>&1 || true
            systemctl start "${svc}" > /dev/null 2>&1 || true
            started_services+=("${svc}")
        fi
    done

    if [[ ${#started_services[@]} -gt 0 ]]; then
        echo "[INFO] >>> [4/5] Service status:"
        sleep 2
        for svc in "${started_services[@]}"; do
            if systemctl is-active --quiet "${svc}"; then
                echo "[INFO] ${svc}: running"
            else
                echo "[WARN] ${svc}: NOT active  (diagnose: systemctl status ${svc})"
            fi
        done
    fi
    if [[ ${#failed_pkgs[@]} -gt 0 ]]; then
        for pkg in "${failed_pkgs[@]}"; do
            echo "[EROR] ${pkg}: NOT INSTALLED (installation failed)"
        done
    fi

    if [[ "$sel" == *" evi-analytics "* ]]; then
        if [[ -z "${ANALYTICS_TYPE}" ]]; then
            echo "[INFO] >>> [5/5] EVI Analytics skipped."
        else
            echo "[INFO] >>> [5/5] Installing EVI Analytics (${ANALYTICS_TYPE})..."
            case "${ANALYTICS_TYPE}" in
                gpu)
                    if [[ -f "${FLAG_DRIVER_OK}" ]] || nvidia-smi > /dev/null 2>&1; then
                        touch "${FLAG_DRIVER_OK}"
                        install_analytics_gpu
                    elif [[ -f "${FLAG_REBOOT_NEEDED}" ]] && [[ -f "${FLAG_REBOOTED}" ]]; then
                        echo "[EROR] NVIDIA driver not working after reboot."
                        rm -f "${FLAG_REBOOT_NEEDED}" "${FLAG_DRIVER_OK}" "${FLAG_ANALYTICS_TYPE}" "${FLAG_REBOOTED}"

                        local fallback_choice=0
                        whiptail --title "EVI PLATFORM ${EVI_VERSION} - GPU Error" \
                            --yesno "X NVIDIA driver is not working after reboot.\n\nPossible reasons:\n  - No NVIDIA GPU in this machine\n  - Driver installation failed\n\nSwitch to CPU Analytics and continue?" \
                            13 65 || fallback_choice=$?

                        if [[ $fallback_choice -eq 0 ]]; then
                            echo "[INFO] Switching to CPU Analytics..."
                            install_analytics_cpu
                        else
                            echo "[EROR] Installation cancelled. To retry GPU, reinstall the driver manually."
                            exit 1
                        fi
                    elif [[ -f "${FLAG_REBOOT_NEEDED}" ]]; then
                        echo "[WARN] NVIDIA driver is installed but the system has not been rebooted yet."
                        echo "[INFO] Please reboot and run the script again to complete GPU Analytics installation."
                        exit 1
                    else
                        if [[ "${VIDEOCARD}" -eq 0 ]]; then
                            local gpu_confirm=0
                            whiptail --title "EVI PLATFORM ${EVI_VERSION} - GPU Warning" \
                                --yesno "WARNING: No NVIDIA GPU detected.\n\nInstalling the driver on a machine without GPU hardware will likely fail, and after reboot analytics will not start.\n\nAre you sure you want to continue?" \
                                12 65 || gpu_confirm=$?
                            if [[ $gpu_confirm -ne 0 ]]; then
                                echo "[WARN] GPU analytics installation cancelled."
                                exit 0
                            fi
                        fi
                        _install_nvidia_driver
                        _install_docker "gpu"
                        if whiptail --title "Reboot required" \
                            --yesno "NVIDIA driver and Docker installed.\nA reboot is required to load the driver.\nAfter reboot, run the script again and select EVI-ANALYTICS.\n\nReboot now?" \
                            11 65; then
                            echo "[INFO] Rebooting..."
                            touch "${FLAG_REBOOTED}"
                            reboot
                            exit 0
                        else
                            echo "[WARN] Reboot cancelled. GPU Analytics installation is paused."
                            echo "[INFO] To complete the installation:"
                            echo "[INFO]   1. Reboot the system manually"
                            echo "[INFO]   2. Run this script again - it will automatically continue with GPU Analytics"
                            exit 1
                        fi
                    fi
                    ;;
                cpu)
                    install_analytics_cpu
                    ;;
                *)
                    echo "[WARN] ANALYTICS_TYPE='${ANALYTICS_TYPE}' is not recognized - skipping analytics installation."
                    ;;
            esac
        fi
    fi
}

########################################################################
# Main flow

main() {
    _detect_gpu

    if [[ -f "${FLAG_REBOOT_NEEDED}" ]] && [[ -f "${FLAG_ANALYTICS_TYPE}" ]]; then
        local saved_type
        saved_type=$(cat "${FLAG_ANALYTICS_TYPE}")

        if [[ "${saved_type}" != "cpu" && "${saved_type}" != "gpu" ]]; then
            echo "[WARN] Invalid saved analytics type '${saved_type}' - restarting selection"
            rm -f "${FLAG_REBOOT_NEEDED}" "${FLAG_ANALYTICS_TYPE}" "${FLAG_REBOOTED}"
            _show_components_menu
        else
            if ! whiptail --title "EVI PLATFORM ${EVI_VERSION} — Continuing after reboot" \
                --yesno "The system was rebooted to apply the NVIDIA driver.\n\nReady to continue installation of ${saved_type^^} Analytics.\n\nPress Continue to proceed or Cancel to abort." \
                10 65 --yes-button "Continue" --no-button "Cancel"; then
                echo "[INFO] Post-reboot analytics installation cancelled by user."
                rm -f "${FLAG_REBOOT_NEEDED}" "${FLAG_ANALYTICS_TYPE}" "${FLAG_REBOOTED}"
                exit 0
            fi
            SELECTED_COMPONENTS="evi-analytics"
            ANALYTICS_TYPE="${saved_type}"
            export ANALYTICS_TYPE
            echo "[INFO] Restored analytics type from previous session: ${ANALYTICS_TYPE}"
        fi
    else
        _show_components_menu
    fi

    if [[ -z "$SELECTED_COMPONENTS" ]]; then
        echo "[EROR] Installation of EVI ${EVI_VERSION} was cancelled by the user."
        exit 0
    fi

    echo "[INFO] Selected components: ${SELECTED_COMPONENTS}"

    local sel_pad=" ${SELECTED_COMPONENTS} "
    if [[ "$sel_pad" == *" evi-analytics "* ]] && [[ -z "${ANALYTICS_TYPE}" ]]; then
        _show_analytics_menu
        if [[ -n "${ANALYTICS_TYPE}" ]]; then
            mkdir -p "${STATE_DIR}"
            echo "${ANALYTICS_TYPE}" > "${FLAG_ANALYTICS_TYPE}"
        fi
    elif [[ -f "${FLAG_REBOOT_NEEDED}" ]] && [[ "$sel_pad" != *" evi-analytics "* ]]; then
        echo "[INFO] EVI Analytics deselected after reboot - clearing reboot flags"
        rm -f "${FLAG_REBOOT_NEEDED}" "${FLAG_ANALYTICS_TYPE}" "${FLAG_REBOOTED}"
    fi

    _check_evi_installed

    install_selected "${SELECTED_COMPONENTS}"

    if [[ "$sel_pad" == *" evi-core-web "* ]] \
       || dpkg -s "evi-core-web" &>/dev/null 2>&1 \
       || rpm -q "evi-core-web" &>/dev/null 2>&1; then
        echo -e "\n[INFO] WEB app is available at https://${EVI_LIVE_EXTERN_HOST}"
        echo "[INFO] user: admin"
        echo "[INFO] password: password"
    fi

    echo "[INFO] Documentation: https://docs.eltex-co.ru/display/en/EVI+Platform"
    echo "[INFO] Installation complete"

    rm -rf "${DOWNLOAD_DIR}" "${STATE_DIR}" 2>/dev/null || true
}

########################################################################
# Entry point

prepare
main
