#!/bin/bash
set -euo pipefail

# =============================================================================
# Metrici Ubuntu 24.04 Installation Script v4.0
# =============================================================================
# Features:
#   - Strict error handling (critical steps stop installation)
#   - Visual progress bar (ASCII text in console)
#   - State tracking (.metrici-install-state.json)
#   - Resume capability (--resume flag)
#   - Colored summary report
#   - MySQL: metrici_sql/ files (local next to script, or downloaded from
#     support server to ~/Downloads/metrici_sql), structure sync (integrated
#     from metrici_db_sync.sh), refresh of general_level_menus + general_translations
# =============================================================================

# --- Colors ---
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly CYAN='\033[0;36m'
readonly BOLD='\033[1m'
readonly NC='\033[0m' # No Color

# --- Configuration ---
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
readonly HOME_DIR="${HOME}"
readonly METRICI_DIR="${HOME_DIR}/metrici"
readonly METRICI_OBJECTS_DIR="${METRICI_DIR}/objects"
readonly METRICI_DB_NAME="metrici2"
readonly METRICI_DB_USER="root"
readonly METRICI_DB_PASS="metriciadmin"
readonly TIMEZONE="$(realpath /etc/localtime | awk -F'/usr/share/zoneinfo/' '{ print $2 }')"
readonly DOWNLOAD_BASE="https://support.metrici.ro/ubuntu2404"
readonly DOWNLOAD_TIMEOUT=120
readonly DOWNLOAD_RETRIES=3
readonly DOWNLOAD_RETRY_DELAY=5
METRICI_SQL_DIR="${SCRIPT_DIR}/metrici_sql"

# --- State File ---
readonly STATE_FILE="${SCRIPT_DIR}/.metrici-install-state.json"

# --- Progress ---
# Text-only progress bar, no zenity
readonly VERBOSE_LOG="${SCRIPT_DIR}/install-verbose.log"

# --- Step Tracking ---
declare -A STEP_STATUS
declare -A STEP_CRITICAL
declare -a STEP_ORDER=()
TOTAL_STEPS=0
CURRENT_STEP=0
FAILED_CRITICAL=""

# --- Sudo Keepalive ---
SUDO_KEEPALIVE_PID=""
IS_ROOT=false  # Will be set to true if running as root

# =============================================================================
# State Management
# =============================================================================

init_state_file() {
    if [[ ! -f "$STATE_FILE" ]]; then
        echo "" > "$STATE_FILE"  # placeholder, will be written at end
    fi
}

load_state() {
    if [[ -f "$STATE_FILE" ]] && [[ -s "$STATE_FILE" ]]; then
        # Parse step statuses from JSON (simple grep/sed, no jq required)
        local step_name
        local status
        # Extract all step_NN entries and their statuses
        while IFS= read -r line; do
            # Match lines like:    "step_validate_dependencies": "success",
            if [[ "$line" =~ \"step_[^\"]+\":\ *\"([^\"]+)\" ]]; then
                step_name=$(echo "$line" | grep -oP '"step_\K[^"]+(?=")')
                status="${BASH_REMATCH[1]}"
                STEP_STATUS["$step_name"]="$status"
            fi
        done < "$STATE_FILE"
    fi
}

update_step_status() {
    local step_name="$1"
    local status="$2"
    STEP_STATUS["$step_name"]="$status"
}

write_state_file() {
    local started_at="$1"
    local completed_at="$2"

    {
        echo "{"
        echo "  \"version\": \"4.1\","
        echo "  \"started_at\": \"${started_at}\","
        echo "  \"completed_at\": \"${completed_at}\","
        echo "  \"steps\": {"

        local total=${#STEP_ORDER[@]}
        local current=0

        for step_name in "${STEP_ORDER[@]}"; do
            current=$((current + 1))
            local status="${STEP_STATUS[$step_name]:-pending}"
            if [[ $current -lt $total ]]; then
                echo "    \"${step_name}\": \"${status}\","
            else
                echo "    \"${step_name}\": \"${status}\""
            fi
        done

        echo "  }"
        echo "}"
    } > "$STATE_FILE"
}

# =============================================================================
# Progress Bar (text only, in console) — functions defined in Logging section
# =============================================================================

SPINNER_PID=""

init_progress_bar() {
    :
}

update_progress() {
    :
}

start_spinner() {
    local text="$1"
    (
        local frames=('|' '/' '\' '-')
        local i=0
        while true; do
            printf "\r  [%s] %s..." "${frames[$i]}" "$text" >/dev/tty
            i=$(( (i + 1) % 4 ))
            sleep 0.3
        done
    ) &
    SPINNER_PID=$!
}

stop_spinner() {
    if [[ -n "$SPINNER_PID" ]]; then
        kill "$SPINNER_PID" 2>/dev/null || true
        wait "$SPINNER_PID" 2>/dev/null || true
        SPINNER_PID=""
        # Clear the spinner line
        printf "\r\033[K" >/dev/tty
    fi
}

# =============================================================================
# Logging - messages go to terminal (fd 4), command output goes to log file
# =============================================================================

log_info()    { echo -e "${BLUE}[INFO]${NC} $(date '+%H:%M:%S') - $*" >&4; }
log_error()   { echo -e "${RED}[ERROR]${NC} $(date '+%H:%M:%S') - $*" >&4; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%H:%M:%S') - $*" >&4; }
log_warn()    { echo -e "${YELLOW}[WARN]${NC} $(date '+%H:%M:%S') - $*" >&4; }
log_step()    { echo "" >&4; echo -e "${BOLD}${CYAN}========== $* ==========${NC}" >&4; }

# Progress bar goes to terminal (fd 4)
show_step_result() {
    local step_num="$1"
    local step_name="$2"
    local total="$3"
    local status="$4"  # "success", "failed", or "skipped"
    local percentage=$(( (step_num * 100) / total ))
    local timestamp
    timestamp=$(date '+%H:%M:%S')

    local status_str
    case "$status" in
        success) status_str="${GREEN}[SUCCESS]${NC}" ;;
        failed)  status_str="${RED}[FAILED]${NC}" ;;
        skipped) status_str="${YELLOW}[SKIP ]${NC}" ;;
    esac

    printf "  %s - %-30s %b %3d%%\n" "$timestamp" "$step_name" "$status_str" "$percentage" >/dev/tty
}

close_progress_bar() {
    echo "" >/dev/tty
}

# =============================================================================
# Error Handling
# =============================================================================

cleanup() {
    local exit_code=$?
    # Kill sudo keepalive
    if [[ -n "$SUDO_KEEPALIVE_PID" ]]; then
        kill "$SUDO_KEEPALIVE_PID" 2>/dev/null || true
        wait "$SUDO_KEEPALIVE_PID" 2>/dev/null || true
    fi
    # Clean up MySQL DB sync temp resources
    dbsync_cleanup
    close_progress_bar

    if [[ $exit_code -ne 0 ]]; then
        echo "" >&4
        log_error "Script failed with exit code: $exit_code"
        if [[ -n "$FAILED_CRITICAL" ]]; then
            log_error "Critical step failed: $FAILED_CRITICAL"
        fi
        log_error "Use --resume to continue from where it stopped"
        log_error "State saved to: $STATE_FILE"
    fi
    exit $exit_code
}

trap cleanup EXIT

# =============================================================================
# Step Name Formatting
# =============================================================================

step_display_name() {
    # Convert "step_validate_dependencies" to "Validate Dependencies"
    local name="$1"
    # Remove "step_" prefix
    name="${name#step_}"
    # Replace underscores with spaces
    name="${name//_/ }"
    # Capitalize first letter of each word
    name=$(echo "$name" | sed 's/\b\(.\)/\u\1/g')
    echo "$name"
}

# =============================================================================
# Step Registration
# =============================================================================

register_step() {
    local name="$1"
    local critical="$2"  # "true" or "false"

    STEP_ORDER+=("$name")
    STEP_CRITICAL["$name"]="$critical"
    STEP_STATUS["$name"]="${STEP_STATUS[$name]:-pending}"
    TOTAL_STEPS=$((TOTAL_STEPS + 1))
}

should_skip_step() {
    local step_name="$1"
    local status="${STEP_STATUS[$step_name]:-pending}"
    [[ "$status" == "success" ]]
}

is_step_critical() {
    local step_name="$1"
    [[ "${STEP_CRITICAL[$step_name]:-false}" == "true" ]]
}

# =============================================================================
# Download Helper (Strict)
# =============================================================================

download_file() {
    local url="$1"
    local output_file="${2:-}"
    local filename
    filename=$(basename "$url")

    if [[ -n "$output_file" ]]; then
        filename="$output_file"
    fi

    log_info "Downloading: $filename"

    local attempt=0
    while (( attempt < DOWNLOAD_RETRIES )); do
        attempt=$((attempt + 1))

        if timeout "$DOWNLOAD_TIMEOUT" wget -q --timeout=60 --tries=1 -O "$filename" "$url"; then
            if [[ -f "$filename" ]] && [[ -s "$filename" ]]; then
                log_info "Downloaded: $filename"
                return 0
            fi
        fi

        log_warn "Download attempt $attempt/$DOWNLOAD_RETRIES failed for: $filename"
        rm -f "$filename"

        if (( attempt < DOWNLOAD_RETRIES )); then
            log_info "Retrying in ${DOWNLOAD_RETRY_DELAY}s..."
            sleep "$DOWNLOAD_RETRY_DELAY"
        fi
    done

    log_error "Failed to download: $filename after $DOWNLOAD_RETRIES attempts"
    return 1
}

# Download multiple files, fail on first error
download_files_strict() {
    local -a urls=("$@")
    for url in "${urls[@]}"; do
        if ! download_file "$url"; then
            return 1
        fi
    done
    return 0
}

# Download multiple files, collect failures but continue
download_files_optional() {
    local -a urls=("$@")
    local -a failed=()
    for url in "${urls[@]}"; do
        if ! download_file "$url"; then
            failed+=("$(basename "$url")")
        fi
    done
    if [[ ${#failed[@]} -gt 0 ]]; then
        log_warn "Failed to download: ${failed[*]}"
    fi
    return 0
}

# =============================================================================
# GPU Detection
# =============================================================================

detect_vendor() {
    local card="$1"
    if echo "$card" | grep -q "NVIDIA"; then
        echo "nvidia"
    elif echo "$card" | grep -q "Intel"; then
        echo "intel"
    elif echo "$card" | grep -q "AMD"; then
        echo "amd"
    else
        echo "unknown"
    fi
}

install_intel_drivers() {
    log_step "Installing Intel GPU drivers"
    wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | \
        sudo gpg --yes --dearmor --output /usr/share/keyrings/intel-graphics.gpg
    echo "deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] \
https://repositories.intel.com/gpu/ubuntu noble client" | \
        sudo tee /etc/apt/sources.list.d/intel-gpu-noble.list >/dev/null
    sudo apt update -y
    sudo apt-get install -y libze-intel-gpu1 libze1 intel-opencl-icd intel-gsc \
        libze-dev intel-ocloc
}

install_nvidia_drivers() {
    log_step "Installing NVIDIA GPU drivers"
    sudo apt-get install -y nvidia-driver-590-open libnvidia-compute-590
}

install_amd_drivers() {
    log_step "Installing AMD GPU drivers"
    local deb_file="amdgpu-install_7.2.70200-1_all.deb"
    download_file "https://repo.radeon.com/amdgpu-install/25.35/ubuntu/noble/${deb_file}"
    sudo apt-get install -y "./${deb_file}"
    sudo apt update -y
    sudo amdgpu-install -y --usecase=opencl --opencl=rocr
    rm -f "${deb_file}"
}

# =============================================================================
# MySQL DB Sync (integrated from metrici_sql/metrici_db_sync.sh)
# -----------------------------------------------------------------------------
# Sincronizeaza STRUCTURA bazei de date cu definitiile din fisierele metrici_*.sql
# (fisierele = sursa de adevar, DB = tinta). Fara a sterge datele existente.
#
# Comportament implicit (sigur, aditiv):
#   - creeaza tabelele lipsa  (CREATE TABLE)
#   - adauga coloanele lipsa  (ADD COLUMN, definitie completa)
#   - adauga indexurile lipsa (ADD INDEX/UNIQUE/FULLTEXT/SPATIAL, definitie completa)
#   - RAPORTEAZA diferentele pe coloanele/indexurile existente (fara a le modifica)
#
# Cu DBSYNC_APPLY_COLUMN_CHANGES / DBSYNC_APPLY_INDEX_CHANGES, diferentele pe
# elementele existente sunt aplicate (MODIFY COLUMN / refacere indexuri).
# =============================================================================

readonly DBSYNC_DB_HOST="localhost"
readonly DBSYNC_DB_PORT="3306"
readonly DBSYNC_APPLY_COLUMN_CHANGES=1
readonly DBSYNC_APPLY_INDEX_CHANGES=1

DBSYNC_SQL_DIR=""
DBSYNC_DB_USER=""
DBSYNC_DB_PASS=""
DBSYNC_DB_NAME=""
DBSYNC_WORK_DIR=""
DBSYNC_MYSQL_CNF=""

# Contoare (in shell-ul principal, NU in subshell)
DBSYNC_TABLES_TOTAL=0; DBSYNC_TABLES_CREATED=0
DBSYNC_COLS_OK=0; DBSYNC_COLS_ADDED=0; DBSYNC_COLS_CHANGED=0; DBSYNC_COLS_REPORTED=0
DBSYNC_IDX_OK=0; DBSYNC_IDX_ADDED=0; DBSYNC_IDX_CHANGED=0; DBSYNC_IDX_REPORTED=0
DBSYNC_ACTIONS=0; DBSYNC_FAILURES=0

dbsync_cleanup() {
    [[ -n "$DBSYNC_MYSQL_CNF" && -f "$DBSYNC_MYSQL_CNF" ]] && rm -f "$DBSYNC_MYSQL_CNF"
    [[ -n "$DBSYNC_WORK_DIR" && -d "$DBSYNC_WORK_DIR" ]] && rm -rf "$DBSYNC_WORK_DIR"
    return 0
}

dbsync_init() {
    local sql_dir="$1" db_user="$2" db_pass="$3" db_name="$4"

    DBSYNC_SQL_DIR="$sql_dir"
    DBSYNC_DB_USER="$db_user"
    DBSYNC_DB_PASS="$db_pass"
    DBSYNC_DB_NAME="$db_name"

    DBSYNC_WORK_DIR=$(mktemp -d)
    dbsync_setup_awk_helpers

    # Credențiale scrise intr-un defaults-file temporar (chmod 600) => parola nu ajunge pe cmdline/ps
    DBSYNC_MYSQL_CNF=$(mktemp)
    chmod 600 "$DBSYNC_MYSQL_CNF"
    {
        printf '[client]\n'
        printf 'user=%s\n'     "$db_user"
        printf 'password=%s\n' "$db_pass"
        printf 'host=%s\n'     "$DBSYNC_DB_HOST"
        printf 'port=%s\n'     "$DBSYNC_DB_PORT"
    } > "$DBSYNC_MYSQL_CNF"

    log_info "Credențiale sync: user=$db_user, db=$db_name, host=$DBSYNC_DB_HOST:$DBSYNC_DB_PORT"
}

# Interogare fara DB implicit / Interogare in baza $DBSYNC_DB_NAME
dbsync_sql()    { mysql --defaults-extra-file="$DBSYNC_MYSQL_CNF" --batch --skip-column-names --silent -e "$1"; }
dbsync_sql_db() { mysql --defaults-extra-file="$DBSYNC_MYSQL_CNF" --batch --skip-column-names --silent "$DBSYNC_DB_NAME" -e "$1"; }

dbsync_check_connection() {
    log_info "Verificare conexiune..."
    if ! dbsync_sql "SELECT 1" >/dev/null 2>&1; then
        log_error "Nu se poate conecta la MySQL. Verificați credențialele."
        return 1
    fi
    local exists
    exists=$(dbsync_sql "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='$DBSYNC_DB_NAME'" 2>/dev/null || true)
    if [[ "$exists" != "$DBSYNC_DB_NAME" ]]; then
        log_warn "Baza '$DBSYNC_DB_NAME' nu exista - se creeaza..."
        dbsync_sql "CREATE DATABASE IF NOT EXISTS \`$DBSYNC_DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci"
        log_success "Baza '$DBSYNC_DB_NAME' creata."
    fi
    log_success "Conexiune stabilita la '$DBSYNC_DB_NAME'."
}

# dbsync_run_sql "<descriere>" "<sql>" - executa cu log + contor esecuri
dbsync_run_sql() {
    local desc="$1" sqltext="$2"
    local out
    if out=$(dbsync_sql_db "$sqltext" 2>&1); then
        log_success "  $desc"
        DBSYNC_ACTIONS=$((DBSYNC_ACTIONS+1))
        return 0
    else
        log_error "  $desc - EȘUE"
        log_error "    SQL : $sqltext"
        log_error "    EROARE: $out"
        DBSYNC_FAILURES=$((DBSYNC_FAILURES+1))
        return 1
    fi
}

# =============================================================================
# Helper awk (scrie programele intr-un fisier, cu heredoc quoted - fara quoting)
# =============================================================================
dbsync_setup_awk_helpers() {
    # items.awk: extrage body-ul CREATE TABLE, il imparte pe itemi top-level
    # (cu tracking paranteze + ghilimele) si clasifica fiecare:
    #   COL|<nume>|<definitie-completa>
    #   IDX|<nume>|<KIND>|<coloane-normalizate>   KIND in {PRIMARY,UNIQUE,KEY,FULLTEXT,SPATIAL}
    #   RAW|<element>                              (CONSTRAINT / necunoscut)
    cat > "$DBSYNC_WORK_DIR/items.awk" <<'AWK'
    function trim(s) { gsub(/^[ \t]+/,"",s); gsub(/[ \t]+$/,"",s); return s }

    # continut primei perechi de paranteze echilibrate (aware de ghilimele)
    function extract_paren(s,    st, d, qs, qd, nn, ii, ch, ep) {
        st = index(s, "(")
        if (st == 0) return ""
        d=0; qs=0; qd=0; nn=length(s); ep=0
        for (ii=st; ii<=nn; ii++) {
            ch = substr(s, ii, 1)
            if (qs) { if (ch=="'" && substr(s,ii-1,1)!="\\") qs=0; continue }
            if (qd) { if (ch=="\"" && substr(s,ii-1,1)!="\\") qd=0; continue }
            if (ch=="'") { qs=1; continue }
            if (ch=="\"") { qd=1; continue }
            if (ch=="(") d++
            else if (ch==")") { d--; if (d==0) { ep=ii; break } }
        }
        if (ep == 0) return ""
        return substr(s, st+1, ep-st-1)
    }

    # normalizare coloane index: fara backtick, fara spatiu la virgula, pastram prefixele (N)
    function norm_cols(s) {
        gsub(/`/,"",s)
        gsub(/[ \t]*,[ \t]*/,",",s)
        return trim(s)
    }

    # numele indexului: cuvantul intre backtick-uri dupa KEY/INDEX/FULLTEXT/SPATIAL
    function extract_idx_name(s,    p) {
        if (match(s, /(KEY|INDEX|FULLTEXT|SPATIAL)[ \t]+`[^`]+`/)) {
            p = substr(s, RSTART, RLENGTH)
            sub(/^[^`]*`/,"",p)      # pana la primul backtick (inclusiv)
            sub(/`.*$/,"",p)         # de la primul backtick ramas pana la final
            return p
        }
        return ""
    }

    # numele coloanei: primul cuvint intre backtick-uri (sau primul cuvint)
    function extract_col_name(s,    p) {
        if (match(s, /^[ \t]*`[^`]+`/)) {
            p = substr(s, RSTART, RLENGTH); gsub(/`/,"",p); return trim(p)
        }
        if (match(s, /^[ \t]*[A-Za-z_][A-Za-z0-9_]*/))
            return trim(substr(s, RSTART, RLENGTH))
        return ""
    }

    function classify(item,    up, nm, co) {
        up = toupper(item)
        if (up ~ /^ *PRIMARY[ \t]+KEY/)
            printf "IDX|PRIMARY|PRIMARY|%s\n", norm_cols(extract_paren(item))
        else if (up ~ /^ *UNIQUE[ \t]+(KEY|INDEX)/)
            printf "IDX|%s|UNIQUE|%s\n", extract_idx_name(item), norm_cols(extract_paren(item))
        else if (up ~ /^ *(KEY|INDEX)[ \t]/)
            printf "IDX|%s|KEY|%s\n", extract_idx_name(item), norm_cols(extract_paren(item))
        else if (up ~ /^ *FULLTEXT/)
            printf "IDX|%s|FULLTEXT|%s\n", extract_idx_name(item), norm_cols(extract_paren(item))
        else if (up ~ /^ *SPATIAL/)
            printf "IDX|%s|SPATIAL|%s\n", extract_idx_name(item), norm_cols(extract_paren(item))
        else if (up ~ /^ *CONSTRAINT/)
            printf "RAW|%s\n", item
        else {
            nm = extract_col_name(item)
            if (nm != "") printf "COL|%s|%s\n", nm, item
            else printf "RAW|%s\n", item
        }
    }

    { body = body $0 "\n" }
    END {
        start = index(body, "(")
        if (start == 0) exit 0
        depth=0; in_s=0; in_d=0; n=length(body); endpos=0
        for (i=start; i<=n; i++) {
            c = substr(body, i, 1)
            if (in_s) { if (c == "'"  && substr(body, i-1, 1) != "\\") in_s=0; continue }
            if (in_d) { if (c == "\"" && substr(body, i-1, 1) != "\\") in_d=0; continue }
            if (c == "'")  { in_s=1; continue }
            if (c == "\"") { in_d=1; continue }
            if (c == "(") depth++
            else if (c == ")") { depth--; if (depth==0) { endpos=i; break } }
        }
        if (endpos == 0) exit 0
        inner = substr(body, start+1, endpos-start-1)
        depth=0; in_s=0; in_d=0; item=""; n2=length(inner)
        for (i=1; i<=n2; i++) {
            c = substr(inner, i, 1)
            if (in_s) { if (c == "'"  && substr(inner, i-1, 1) != "\\") in_s=0; item=item c; continue }
            if (in_d) { if (c == "\"" && substr(inner, i-1, 1) != "\\") in_d=0; item=item c; continue }
            if (c == "'")  { in_s=1; item=item c; continue }
            if (c == "\"") { in_d=1; item=item c; continue }
            if (c == "(") { depth++; item=item c; continue }
            if (c == ")") { depth--; item=item c; continue }
            if (c == "," && depth==0) {
                gsub(/\n/," ",item); gsub(/[ \t]+/," ",item)
                sub(/^ /,"",item); sub(/ $/,"",item)
                if (item != "") classify(item)
                item=""; continue
            }
            item=item c
        }
        gsub(/\n/," ",item); gsub(/[ \t]+/," ",item)
        sub(/^ /,"",item); sub(/ $/,"",item)
        if (item != "") classify(item)
    }
AWK
}

# =============================================================================
# Parsare fisier SQL -> blocuri CREATE TABLE (un fisier temp per tabel)
# =============================================================================
dbsync_split_sql_file() {
    local sql_file="$1" outdir="$2"
    awk -v outdir="$outdir" '
        function flush() {
            if (buf != "") {
                n++
                printf "%s\n", tbl > (outdir "/" sprintf("%04d", n) ".name")
                printf "%s\n", buf > (outdir "/" sprintf("%04d", n) ".sql")
                buf = ""; tbl = ""
            }
        }
        {
            line = $0
            if (line ~ /^[[:space:]]*DROP TABLE/) next
            if (line ~ /^[[:space:]]*CREATE TABLE/) {
                flush()
                buf = line
                tbl = line
                sub(/^[[:space:]]*CREATE TABLE[[:space:]]+IF[[:space:]]+NOT[[:space:]]+EXISTS[[:space:]]+/, "", tbl)
                sub(/^[[:space:]]*CREATE TABLE[[:space:]]+/, "", tbl)
                sub(/^`/, "", tbl)
                sub(/`.*/, "", tbl)
                sub(/[[:space:]].*$/, "", tbl)
                next
            }
            if (buf != "") {
                buf = buf " " line
                if (line ~ /ENGINE=/ && line ~ /;[[:space:]]*$/) flush()
            }
        }
        END { flush() }
    ' "$sql_file"
}

# parse_block <fisier-bloc> -> linii structurate (awk face despartirea + clasificarea):
#   COL|<nume>|<definitie-completa>
#   IDX|<nume>|<KIND>|<coloane-normalizate>   KIND in {PRIMARY,UNIQUE,KEY,FULLTEXT,SPATIAL}
#   RAW|<element>                              (CONSTRAINT / necunoscut)
dbsync_parse_block() {
    awk -f "$DBSYNC_WORK_DIR/items.awk" "$1"
}

# =============================================================================
# Intrebari catre DB
# =============================================================================

# Batching: tot metadata-ul DB in 3 interogari unice (nu cate una pe tabela).
# Ieseri (fișiere temp umplute de dbsync_sync_database):
#   all_tables : o linie pe tabela  ->  <table>
#   all_cols   : COL|<table>|<name>|<type>|<null(YES/NO)>|<extra>
#   all_idx    : IDX|<table>|<name>|<nonunique(0/1)>|<cols(normalizate, cu prefixe)>
dbsync_fetch_all_metadata() {
    local tdir="$1"
    dbsync_sql "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='$DBSYNC_DB_NAME'" 2>/dev/null \
        > "$tdir/all_tables" || true

    dbsync_sql "SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, IFNULL(EXTRA,'')
         FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$DBSYNC_DB_NAME'
         ORDER BY TABLE_NAME, ORDINAL_POSITION" 2>/dev/null \
    | awk -F'\t' 'NF>=5 {printf "COL|%s|%s|%s|%s|%s\n", $1,$2,$3,$4,$5}' > "$tdir/all_cols"

    dbsync_sql "SELECT TABLE_NAME, INDEX_NAME, NON_UNIQUE,
                GROUP_CONCAT(CONCAT(COLUMN_NAME, IF(SUB_PART IS NULL, '', CONCAT('(', SUB_PART, ')'))) ORDER BY SEQ_IN_INDEX SEPARATOR ',')
         FROM information_schema.STATISTICS WHERE TABLE_SCHEMA='$DBSYNC_DB_NAME'
         GROUP BY TABLE_NAME, INDEX_NAME, NON_UNIQUE" 2>/dev/null \
    | awk -F'\t' 'NF>=4 {printf "IDX|%s|%s|%s|%s\n", $1,$2,$3,$4}' > "$tdir/all_idx"
}

# Tipul coloanei din definitia SQL (bash pur, fara subprocese).
# Ex: "`name` varchar(256) NOT NULL" -> "varchar(256)"
#     "`id` bigint unsigned NOT NULL AUTO_INCREMENT" -> "bigint unsigned"
#     "`x` decimal(10,2) NOT NULL" -> "decimal(10,2)"
dbsync_sql_column_type() {
    local item="$1"
    item="${item,,}"                                  # lowercase
    # potrivim: [spatii] `? nume `? [spatii] TIP  (TIP = baza + (..)? + unsigned/zerofill?)
    local re='^[[:space:]]*`?[a-zA-Z_][a-zA-Z0-9_]*`?[[:space:]]+([a-z]+(\([^)]*\))?[ ]?(unsigned[ ]+(zerofill[ ]+)?|zerofill[ ]+(unsigned[ ]+)?)?)'
    if [[ "$item" =~ $re ]]; then
        local t="${BASH_REMATCH[1]}"
        t="${t%"${t##*[! ]}"}"                        # rtrim
        printf '%s' "$t"
    fi
}

# =============================================================================
# Sincronizare coloane
# =============================================================================
# existing = linii "COL|<table>|<name>|<type>|<null>|<extra>" pentru tabela respectivă
dbsync_sync_columns() {
    local table="$1" existing="$2" sql_cols="$3"

    local line col_name col_def
    while IFS= read -r line; do
        [[ -z "$line" || "$line" != COL\|* ]] && continue
        IFS='|' read -r _ col_name col_def <<< "$line"
        [[ -z "$col_name" ]] && continue

        # exista? (cautare locala)
        local ex_line=""
        while IFS='|' read -r _ t n ty nl ex; do
            [[ "$t" == "$table" && "$n" == "$col_name" ]] && { ex_line="$ty|$nl|$ex"; break; }
        done <<< "$existing"

        if [[ -z "$ex_line" ]]; then
            log_warn "  coloana '$col_name' lipseste - se adauga"
            if dbsync_run_sql "ADD COLUMN \`$table\`.\`$col_name\`" "ALTER TABLE \`$table\` ADD COLUMN $col_def"; then
                DBSYNC_COLS_ADDED=$((DBSYNC_COLS_ADDED+1))
            fi
        else
            local ex_type ex_null ex_extra sql_type sql_null sql_extra
            IFS='|' read -r ex_type ex_null ex_extra <<< "$ex_line"

            sql_type=$(dbsync_sql_column_type "$col_def")
            if [[ "$col_def" == *"NOT NULL"* ]]; then sql_null="NO"; else sql_null="YES"; fi
            # compar doar flag-ul auto_increment (DEFAULT_GENERATED e efect secundar al DEFAULT-ului)
            local ex_ai=0 sql_ai=0
            [[ "$ex_extra" == *auto_increment* ]] && ex_ai=1
            [[ "$col_def" == *AUTO_INCREMENT* ]]  && sql_ai=1

            local diff=""
            [[ "${sql_type:-}" != "${ex_type:-}" ]] && diff="tip: db='$ex_type' sql='$sql_type'"
            [[ "$sql_null" != "$ex_null" ]]         && diff="${diff} null: db='$ex_null' sql='$sql_null'"
            [[ "$ex_ai" != "$sql_ai" ]]             && diff="${diff} auto_increment: db=$ex_ai sql=$sql_ai"

            if [[ -n "$diff" ]]; then
                if [[ $DBSYNC_APPLY_COLUMN_CHANGES -eq 1 ]]; then
                    log_warn "  coloana '$col_name' diferita ($diff) - se MODIFICA"
                    if dbsync_run_sql "MODIFY COLUMN \`$table\`.\`$col_name\`" "ALTER TABLE \`$table\` MODIFY COLUMN $col_def"; then
                        DBSYNC_COLS_CHANGED=$((DBSYNC_COLS_CHANGED+1))
                    fi
                else
                    log_warn "  coloana '$col_name' diferita ($diff) - DOAR RAPORT"
                    DBSYNC_COLS_REPORTED=$((DBSYNC_COLS_REPORTED+1))
                fi
            else
                DBSYNC_COLS_OK=$((DBSYNC_COLS_OK+1))     # fara log (zgomot); se raporteaza in sumar
            fi
        fi
    done <<< "$sql_cols"
}

# =============================================================================
# Sincronizare indexuri
# =============================================================================
# existing = linii "IDX|<table>|<name>|<nonunique>|<cols>" pentru tabela respectivă
dbsync_sync_indexes() {
    local table="$1" existing="$2" sql_idx="$3"

    local line idx_name idx_type cols formatted
    local idx_repl='`, `'                    # backtick spatiu backtick (literal, fara quoting)
    while IFS= read -r line; do
        [[ -z "$line" || "$line" != IDX\|* ]] && continue
        IFS='|' read -r _ idx_name idx_type cols <<< "$line"
        formatted=${cols//,/$idx_repl}       # a,b -> `a`, `b`  (bash, fara subproces)

        # PRIMARY KEY
        if [[ "$idx_name" == "PRIMARY" ]]; then
            local has_pk=0
            while IFS='|' read -r _ t n nu c; do
                [[ "$t" == "$table" && "$n" == "PRIMARY" ]] && { has_pk=1; break; }
            done <<< "$existing"
            if [[ $has_pk -eq 1 ]]; then
                DBSYNC_IDX_OK=$((DBSYNC_IDX_OK+1))
            else
                log_warn "  PRIMARY KEY lipseste - se adauga"
                if dbsync_run_sql "ADD PRIMARY KEY \`$table\`" "ALTER TABLE \`$table\` ADD PRIMARY KEY (\`$formatted\`)"; then
                    DBSYNC_IDX_ADDED=$((DBSYNC_IDX_ADDED+1))
                fi
            fi
            continue
        fi

        # exista? (cautare locala)
        local has_idx=0 ex_nu="" ex_cols=""
        while IFS='|' read -r _ t n nu c; do
            if [[ "$t" == "$table" && "$n" == "$idx_name" ]]; then has_idx=1; ex_nu="$nu"; ex_cols="$c"; break; fi
        done <<< "$existing"

        if [[ $has_idx -eq 0 ]]; then
            log_warn "  index '$idx_name' lipseste - se adauga"
            local add
            case "$idx_type" in
                UNIQUE)   add="ALTER TABLE \`$table\` ADD UNIQUE INDEX \`$idx_name\` (\`$formatted\`)" ;;
                FULLTEXT) add="ALTER TABLE \`$table\` ADD FULLTEXT INDEX \`$idx_name\` (\`$formatted\`)" ;;
                SPATIAL)  add="ALTER TABLE \`$table\` ADD SPATIAL INDEX \`$idx_name\` (\`$formatted\`)" ;;
                *)        add="ALTER TABLE \`$table\` ADD INDEX \`$idx_name\` (\`$formatted\`)" ;;
            esac
            if dbsync_run_sql "ADD INDEX \`$table\`.\`$idx_name\`" "$add"; then
                DBSYNC_IDX_ADDED=$((DBSYNC_IDX_ADDED+1))
            fi
        else
            local want_nu diff
            if [[ "$idx_type" == "UNIQUE" ]]; then want_nu=0; else want_nu=1; fi
            diff=""
            [[ "$ex_cols" != "$cols" ]] && diff="coloane: db='$ex_cols' sql='$cols'"
            [[ "$ex_nu" != "$want_nu" ]] && diff="${diff} unicitate: db NON_UNIQUE=$ex_nu sql NON_UNIQUE=$want_nu"

            if [[ -n "$diff" ]]; then
                if [[ $DBSYNC_APPLY_INDEX_CHANGES -eq 1 ]]; then
                    log_warn "  index '$idx_name' diferit ($diff) - se refaca (atomic)"
                    local kind
                    case "$idx_type" in
                        UNIQUE) kind="UNIQUE INDEX" ;; FULLTEXT) kind="FULLTEXT INDEX" ;;
                        SPATIAL) kind="SPATIAL INDEX" ;; *) kind="INDEX" ;;
                    esac
                    if dbsync_run_sql "RECREATE INDEX \`$table\`.\`$idx_name\`" \
                        "ALTER TABLE \`$table\` DROP INDEX \`$idx_name\`, ADD $kind \`$idx_name\` (\`$formatted\`)"; then
                        DBSYNC_IDX_CHANGED=$((DBSYNC_IDX_CHANGED+1))
                    fi
                else
                    log_warn "  index '$idx_name' diferit ($diff) - DOAR RAPORT"
                    DBSYNC_IDX_REPORTED=$((DBSYNC_IDX_REPORTED+1))
                fi
            else
                DBSYNC_IDX_OK=$((DBSYNC_IDX_OK+1))     # fara log (zgomot); se raporteaza in sumar
            fi
        fi
    done <<< "$sql_idx"
}

dbsync_report_raw() {
    log_warn "  element CONSTRAINT/necunoscut in '$1' (ignorat, verifica manual): $2"
}

# =============================================================================
# Recreate tabele complete din fisiere (CREATE + INSERT) - pentru tabelele care
# trebuie sa corespunda EXACT cu fisierele (meniuri, traduceri etc.)
# =============================================================================
dbsync_extract_inserts() {
    local sql_file="$1" table="$2" outfile="$3"
    awk -v tbl="$table" '
        BEGIN { marker = "INSERT INTO `" tbl "`" }
        {
            line = $0
            if (in_ins) {
                print line
                if (line ~ /;[ \t]*$/) in_ins = 0
                next
            }
            if (index(line, marker) > 0) {
                in_ins = 1
                print line
                if (line ~ /;[ \t]*$/) in_ins = 0
            }
        }
    ' "$sql_file" > "$outfile"
}

dbsync_recreate_table() {
    local table="$1"
    local f fname sql_file="" name_file create_block="" blocks_dir inserts tmp_sql

    # 1) Gaseste fisierul care contine CREATE TABLE pentru tabela respectiva
    for f in "$DBSYNC_SQL_DIR"/metrici_*.sql; do
        [[ -e "$f" ]] || continue
        fname=$(basename "$f")
        [[ "$fname" == "metrici_db.sql" ]] && continue
        if grep -q "CREATE TABLE \`${table}\`" "$f"; then
            sql_file="$f"
            break
        fi
    done
    if [[ -z "$sql_file" ]]; then
        log_error "Tabela '$table' nu a fost gasita in $DBSYNC_SQL_DIR/metrici_*.sql"
        return 1
    fi
    log_info "Recreate tabela: $table (sursa: $(basename "$sql_file"))"

    # 2) Extrag blocul CREATE TABLE
    blocks_dir="$DBSYNC_WORK_DIR/recreate_${table}_blocks"
    rm -rf "$blocks_dir"; mkdir -p "$blocks_dir"
    dbsync_split_sql_file "$sql_file" "$blocks_dir"
    for name_file in "$blocks_dir"/*.name; do
        [[ -e "$name_file" ]] || continue
        if [[ "$(cat "$name_file")" == "$table" ]]; then
            create_block="${name_file%.name}.sql"
            break
        fi
    done
    if [[ -z "$create_block" ]] || [[ ! -f "$create_block" ]]; then
        log_error "Blocul CREATE TABLE pentru '$table' nu a fost gasit in $(basename "$sql_file")"
        return 1
    fi

    # 3) Extrag INSERT-urile pentru tabela respectiva
    inserts="$DBSYNC_WORK_DIR/recreate_${table}_inserts.sql"
    dbsync_extract_inserts "$sql_file" "$table" "$inserts"
    log_info "  INSERT-uri extrase: $(wc -l < "$inserts") linii"

    # 4) Drop, recreate, re-insert (ca root, cu FK checks oprite)
    tmp_sql="$DBSYNC_WORK_DIR/recreate_${table}.sql"
    {
        echo "SET FOREIGN_KEY_CHECKS=0;"
        echo "DROP TABLE IF EXISTS \`$table\`;"
        cat "$create_block"
        echo ""
        cat "$inserts"
        echo "SET FOREIGN_KEY_CHECKS=1;"
    } > "$tmp_sql"

    if ! sudo mysql --user=root --password="$METRICI_DB_PASS" "$DBSYNC_DB_NAME" < "$tmp_sql"; then
        log_error "Eșec recreate tabela '$table'"
        return 1
    fi
    log_success "Recreate tabela: $table"
    return 0
}

# =============================================================================
# Sincronizare baza
# =============================================================================
dbsync_sync_database() {
    log_info "================ Sincronizare $DBSYNC_DB_NAME ================"

    # 1) Batching: tot metadata-ul DB intr-o singura trecere (3 interogari)
    local meta="$DBSYNC_WORK_DIR/meta"
    mkdir -p "$meta"
    log_info "Preiau metadata DB (tabele/coloane/indexuri)..."
    dbsync_fetch_all_metadata "$meta"

    local sql_files=()
    local f fname
    for f in "$DBSYNC_SQL_DIR"/metrici_*.sql; do
        [[ -e "$f" ]] || continue
        fname=$(basename "$f")
        [[ "$fname" == "metrici_db.sql" ]] && continue
        [[ "$fname" == *_update_* ]] && continue
        sql_files+=("$f")
    done

    if [[ ${#sql_files[@]} -eq 0 ]]; then
        log_warn "Niciun fisier SQL de procesat."
        return 0
    fi
    log_info "Fisiere de procesat: ${#sql_files[@]}"

    local sql_file
    for sql_file in "${sql_files[@]}"; do
        fname=$(basename "$sql_file")
        log_info "----- $fname -----"

        local blocks="$DBSYNC_WORK_DIR/blocks"
        rm -rf "$blocks"; mkdir -p "$blocks"
        dbsync_split_sql_file "$sql_file" "$blocks"

        local name_file
        for name_file in "$blocks"/*.name; do
            [[ -e "$name_file" ]] || continue
            local table block create_stmt parsed sql_cols sql_idx raw_line
            table=$(cat "$name_file")
            block="${name_file%.name}.sql"
            DBSYNC_TABLES_TOTAL=$((DBSYNC_TABLES_TOTAL+1))
            log_info "Tabel: $table"

            # tabela exista? (grep local, fara interogare)
            if ! grep -qx "$table" "$meta/all_tables"; then
                log_warn "  tabela nu exista - se creeaza"
                create_stmt=$(cat "$block")
                if dbsync_run_sql "CREATE TABLE \`$table\`" "$create_stmt"; then
                    DBSYNC_TABLES_CREATED=$((DBSYNC_TABLES_CREATED+1))
                fi
                continue
            fi

            # exista -> filtrez local coloanele/indexurile existente pentru tabela asta
            local existing_cols existing_idx
            existing_cols=$(grep "^COL|$table|" "$meta/all_cols" || true)
            existing_idx=$(grep "^IDX|$table|" "$meta/all_idx" || true)

            parsed=$(dbsync_parse_block "$block")
            sql_cols=$(printf '%s\n' "$parsed" | grep '^COL|' || true)
            sql_idx=$(printf '%s\n' "$parsed" | grep '^IDX|' || true)

            dbsync_sync_columns "$table" "$existing_cols" "$sql_cols"
            dbsync_sync_indexes "$table" "$existing_idx" "$sql_idx"

            while IFS= read -r raw_line; do
                [[ -z "$raw_line" ]] && continue
                [[ "${raw_line%%|*}" == "RAW" ]] && dbsync_report_raw "$table" "${raw_line#RAW|}"
            done <<< "$parsed"
        done
    done

    log_info "================ Finalizat ================"
}

# =============================================================================
# Installation Steps
# =============================================================================

step_validate_dependencies() {
    log_step "Validating dependencies"
    local missing_deps=()
    for cmd in wget sudo apt grep sed awk; do
        if ! command -v "$cmd" &>/dev/null; then
            missing_deps+=("$cmd")
        fi
    done
    if [[ ${#missing_deps[@]} -gt 0 ]]; then
        log_error "Missing dependencies: ${missing_deps[*]}"
        return 1
    fi
    log_success "All dependencies validated"
}

step_configure_gpu_drivers() {
    log_step "Configuring GPU drivers"
    local vga_cards
    vga_cards=$(lspci | grep 'VGA' | grep 'NVIDIA\|Intel\|AMD' || true)

    if [[ -z "$vga_cards" ]]; then
        log_info "No NVIDIA/Intel/AMD VGA cards detected, skipping"
        return 0
    fi

    while IFS= read -r card; do
        [[ -z "$card" ]] && continue
        local vendor
        vendor=$(detect_vendor "$card")
        log_info "Processing: $(echo "$card" | cut -c1-60) (Vendor: $vendor)"

        case "$vendor" in
            nvidia) install_nvidia_drivers ;;
            intel)  install_intel_drivers ;;
            amd)    install_amd_drivers ;;
            *)      log_info "Unknown vendor, skipping" ;;
        esac
    done <<< "$vga_cards"

    sudo usermod -a -G render,video "$(whoami)"
    log_success "GPU drivers configured"
}

step_setup_system_packages() {
    log_step "Installing system packages"

    # Add PPAs (ignore if already present)
    log_info "Adding PPAs..."
    sudo add-apt-repository ppa:quentiumyt/nvtop -y 2>/dev/null || true
    sudo add-apt-repository ppa:ondrej/php -y 2>/dev/null || true
    sudo add-apt-repository ppa:ondrej/apache2 -y 2>/dev/null || true

    # Update and upgrade
    log_info "Updating package lists..."
    sudo apt update -y
    log_info "Upgrading system packages..."
    sudo apt upgrade -y

    # Install packages
    local utils="clinfo screen sqlite3 gpsd modemmanager smstools nvtop mc chrony gedit ssh"
    local qt5="libqt5sql5-sqlite libqt5quick5 libqt5qml5 libqt5network5t64 libqt5widgets5t64"
    local qt5_gstreamer="libqt5gstreamer-1.0-0 libqt5glib-2.0-0 libqt5gstreamerutils-1.0-0"
    local gstreamer="gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-libav gstreamer1.0-pipewire \
        gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly \
        gstreamer1.0-vaapi gstreamer1.0-x libgstreamer-gl1.0-0 \
        libgstreamer-plugins-bad1.0-0 libgstreamer-plugins-base1.0-0 \
        libgstreamer-plugins-good1.0-0 libgstreamer1.0-0 libgtk-4-media-gstreamer"
    local server="apache2 mysql-server memcached curl net-tools smartmontools plocate zbar-tools libzxing3"
    local php="php7.4 php7.4-fpm php7.4-cli php7.4-json php7.4-common php7.4-mysql \
        php7.4-zip php7.4-gd php7.4-mbstring php7.4-mcrypt php7.4-memcache \
        php7.4-curl php7.4-xml php7.4-bcmath php7.4-imagick php-pear"

    log_info "Installing packages..."
    sudo apt-get install -y $utils $qt5 $qt5_gstreamer $gstreamer $server $php
    sudo apt-mark hold php7.4-*

    # Install custom gstreamer1.0-libav (avpacketfix) on top of the repo version
    local libav_deb="gstreamer1.0-libav_1.24.1-avpacketfix1_amd64.deb"
    log_info "Installing custom gstreamer1.0-libav (${libav_deb})..."
    download_file "${DOWNLOAD_BASE}/${libav_deb}"
    sudo apt-get install -y "./${libav_deb}"
    rm -f "${libav_deb}"

    # Configure Apache + PHP
    sudo a2enmod proxy_fcgi setenvif rewrite
    sudo a2enconf php7.4-fpm

    log_success "System packages installed"
}

step_configure_php() {
    log_step "Configuring PHP"

    local ioncube_files=("ioncube_loader_lin_7.4.so" "ioncube_loader_lin_7.4_ts.so")

    # Download IonCube
    for file in "${ioncube_files[@]}"; do
        download_file "${DOWNLOAD_BASE}/${file}"
    done

    sudo mkdir -p /usr/local/ioncube
    for file in "${ioncube_files[@]}"; do
        sudo cp -f "$file" /usr/local/ioncube/
        rm -f "$file"
    done

    # Configure PHP (CLI and FPM)
    local php_configs=("/etc/php/7.4/cli/php.ini" "/etc/php/7.4/fpm/php.ini")

    for config in "${php_configs[@]}"; do
        if ! grep -q "date.timezone" "$config"; then
            sudo sed -i '/\[Date\]/a date.timezone = Europe/Bucharest' "$config"
        fi
        sudo sed -i "s#date\.timezone = .*#date.timezone = ${TIMEZONE}#" "$config"
        sudo sed -i "s/upload_max_filesize = 2M/upload_max_filesize = 2048M/" "$config"
        sudo sed -i "s/post_max_size = 8M/post_max_size = 3072M/" "$config"
        sudo sed -i "s/max_execution_time = 30/max_execution_time = 600/" "$config"
        sudo sed -i "s/memory_limit = -1/memory_limit = 8192M/" "$config"
        sudo sed -i "s/memory_limit = 128M/memory_limit = 8192M/" "$config"
        sudo sed -i "s/short_open_tag = Off/short_open_tag = On/" "$config"
        if ! grep -q "zend_extension" "$config"; then
            echo "zend_extension=/usr/local/ioncube/${ioncube_files[0]}" | sudo tee -a "$config" >/dev/null
        fi
    done

    sudo sed -i "s/pm.max_children = 5/pm.max_children = 400/" /etc/php/7.4/fpm/pool.d/www.conf

    log_success "PHP configured"
}

step_configure_apache() {
    log_step "Configuring Apache"

    download_file "${DOWNLOAD_BASE}/000-default.conf"
    sudo cp -f 000-default.conf /etc/apache2/sites-available/
    rm -f 000-default.conf

    echo "ServerName localhost" | sudo tee /etc/apache2/conf-available/servername.conf >/dev/null
    sudo a2enconf servername

    sudo systemctl restart php7.4-fpm
    sudo systemctl restart apache2

    log_success "Apache configured"
}

step_configure_mysql() {
    log_step "Configuring MySQL"

    # Set root password (fresh install - root has no password)
    sudo mysqladmin -u root password "$METRICI_DB_PASS" 2>/dev/null || true

    # --- Ensure SQL files: local (next to script) or download from support server ---
    if [[ ! -f "${SCRIPT_DIR}/metrici_sql/metrici_db.sql" ]]; then
        METRICI_SQL_DIR="${HOME_DIR}/Downloads/metrici_sql"
        log_info "Preparing SQL directory: ${METRICI_SQL_DIR}"
        mkdir -p "${METRICI_SQL_DIR}"
        local sql_name
        for sql_name in metrici_ac metrici_ccr metrici_cloud metrici_db metrici_eer \
                        metrici_ext metrici_general metrici_lc metrici_lpr metrici_parking \
                        metrici_ppd metrici_qr metrici_ta metrici_toll metrici_vj metrici_weighing; do
            if ! download_file "${DOWNLOAD_BASE}/metrici_sql/${sql_name}.sql" "${METRICI_SQL_DIR}/${sql_name}.sql"; then
                log_error "Cannot continue without SQL files. Check network access to ${DOWNLOAD_BASE}"
                return 1
            fi
        done
    fi

    # --- Verify SQL files ---
    if [[ ! -f "${METRICI_SQL_DIR}/metrici_db.sql" ]]; then
        log_error "metrici_db.sql not found in: ${METRICI_SQL_DIR}"
        log_error "Copy the metrici_sql folder (metrici_db.sql + metrici_*.sql) next to the installer script"
        return 1
    fi
    local sql_file
    local table_files=()
    for sql_file in "${METRICI_SQL_DIR}"/metrici_*.sql; do
        [[ -e "$sql_file" ]] || continue
        [[ "$(basename "$sql_file")" == "metrici_db.sql" ]] && continue
        table_files+=("$sql_file")
    done
    if [[ ${#table_files[@]} -eq 0 ]]; then
        log_error "No table SQL files (metrici_*.sql) found in: ${METRICI_SQL_DIR}"
        return 1
    fi
    log_info "Found ${#table_files[@]} table SQL files in: ${METRICI_SQL_DIR}"

    # --- Extract credentials from metrici_db.sql (single source of truth) ---
    local db_user db_pass db_name
    db_user=$(grep -oP "CREATE USER '\K[^']+" "${METRICI_SQL_DIR}/metrici_db.sql" | head -1 || true)
    db_pass=$(grep -oP "IDENTIFIED BY '\K[^']+" "${METRICI_SQL_DIR}/metrici_db.sql" | head -1 || true)
    db_name=$(grep -oP 'CREATE DATABASE `\K[^`]+' "${METRICI_SQL_DIR}/metrici_db.sql" | head -1 || true)
    if [[ -z "$db_name" ]]; then
        db_name=$(grep -oP 'USE `\K[^`]+' "${METRICI_SQL_DIR}/metrici_db.sql" | head -1 || true)
    fi
    if [[ -z "$db_user" || -z "$db_pass" || -z "$db_name" ]]; then
        log_error "Could not extract user/password/database from: ${METRICI_SQL_DIR}/metrici_db.sql"
        return 1
    fi
    log_info "Database: $db_name | App user: $db_user"

    # --- Create database + app user (idempotent, as root) ---
    sudo mysql --user=root --password="$METRICI_DB_PASS" <<EOF
CREATE DATABASE IF NOT EXISTS \`${db_name}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
CREATE USER IF NOT EXISTS '${db_user}'@'localhost' IDENTIFIED BY '${db_pass}';
GRANT ALL PRIVILEGES ON \`${db_name}\`.* TO '${db_user}'@'localhost';
SET PERSIST sql_mode='NO_AUTO_VALUE_ON_ZERO,ERROR_FOR_DIVISION_BY_ZERO';
FLUSH PRIVILEGES;
EOF

    # --- Determine install mode: fresh (empty DB) or existing ---
    local table_count
    if ! table_count=$(sudo mysql --user=root --password="$METRICI_DB_PASS" --batch --skip-column-names \
        -e "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='${db_name}'"); then
        log_error "Failed to query table count for database: $db_name"
        return 1
    fi

    if [[ "$table_count" -eq 0 ]]; then
        # Fresh install: full import of local files (structure + data)
        log_info "Empty database - fresh install: importing all SQL files (structure + data)..."
        for sql_file in "${table_files[@]}"; do
            log_info "Importing: $(basename "$sql_file")"
            if ! sudo mysql --user=root --password="$METRICI_DB_PASS" \
                --init-command="SET SESSION sql_mode='NO_AUTO_VALUE_ON_ZERO,ERROR_FOR_DIVISION_BY_ZERO';" \
                "${db_name}" < "$sql_file"; then
                log_error "Failed to import: $(basename "$sql_file")"
                return 1
            fi
        done
    else
        # Existing database: refresh fixed tables + structure sync (no data loss)
        log_info "Existing database (${table_count} tables) - sync mode..."

        dbsync_init "${METRICI_SQL_DIR}" "$db_user" "$db_pass" "$db_name"

        # 1) Recreate tables that must always match the files exactly
        local recreate_tables=("general_level_menus" "general_translations")
        local recreate_table
        for recreate_table in "${recreate_tables[@]}"; do
            if ! dbsync_recreate_table "$recreate_table"; then
                return 1
            fi
        done

        # 2) Structure sync (additive, no data loss)
        if ! dbsync_check_connection; then
            return 1
        fi
        dbsync_sync_database

        log_info "------------------------------------------"
        log_info "Tabele procesate : $DBSYNC_TABLES_TOTAL (create: $DBSYNC_TABLES_CREATED)"
        log_info "Coloane          : ok=$DBSYNC_COLS_OK  adaugate=$DBSYNC_COLS_ADDED  modificate=$DBSYNC_COLS_CHANGED  raportate=$DBSYNC_COLS_REPORTED"
        log_info "Indexuri         : ok=$DBSYNC_IDX_OK  adaugate=$DBSYNC_IDX_ADDED  modificate=$DBSYNC_IDX_CHANGED  raportate=$DBSYNC_IDX_REPORTED"
        log_info "Actiuni totale   : $DBSYNC_ACTIONS   Esecuri: $DBSYNC_FAILURES"
        if [[ $DBSYNC_FAILURES -gt 0 ]]; then
            log_error "Sincronizarea structurii s-a finalizat cu $DBSYNC_FAILURES esecuri."
            return 1
        fi
        log_success "Sincronizarea structurii finalizata cu succes."
    fi

    # --- Download and apply MySQL config ---
    download_file "${DOWNLOAD_BASE}/mysqld.cnf"
    sudo cp -f mysqld.cnf /etc/mysql/mysql.conf.d/
    rm -f mysqld.cnf

    sudo systemctl restart mysql

    log_success "MySQL configured"
}

step_configure_memcached() {
    log_step "Configuring Memcached"
    if ! grep -q "CACHESIZE" /etc/default/memcached 2>/dev/null; then
        echo "CACHESIZE=\"2048\"" | sudo tee -a /etc/default/memcached >/dev/null
    else
        # Update existing CACHESIZE
        sudo sed -i 's/CACHESIZE=.*/CACHESIZE="2048"/' /etc/default/memcached
    fi
    log_success "Memcached configured"
}

step_install_opencv() {
    log_step "Installing OpenCV"

    local opencv_packages=(
        "opencv-4.11.0-x86_64-dev.deb"
        "opencv-4.11.0-x86_64-libs.deb"
        "opencv-4.11.0-x86_64-licenses.deb"
        "opencv-4.11.0-x86_64-main.deb"
        "opencv-4.11.0-x86_64-python.deb"
        "opencv-4.11.0-x86_64-scripts.deb"
    )

    log_info "Downloading OpenCV packages..."
    for pkg in "${opencv_packages[@]}"; do
        download_file "${DOWNLOAD_BASE}/${pkg}" || {
            log_warn "Failed to download $pkg, will skip if missing"
        }
    done

    log_info "Installing OpenCV packages..."
    for pkg in "${opencv_packages[@]}"; do
        if [[ -f "$pkg" ]]; then
            log_info "Installing: $pkg"
            sudo apt-get install -y "./${pkg}" 2>/dev/null || {
                log_warn "Failed to install $pkg"
            }
            rm -f "$pkg"
        else
            log_warn "Package not downloaded, skipping: $pkg"
        fi
    done

    log_success "OpenCV installed"
}

step_install_metrici_files() {
    log_step "Installing Metrici files"

    mkdir -p "$METRICI_DIR" "$METRICI_OBJECTS_DIR"

    # Main executables
    local executables=("metrici-ac" "metrici-cpan" "metrici-lc" "metrici-lpr" \
        "metrici-lpr-plus" "metrici-ppd" "metrici-ccr" "metrici-qr")
    log_info "Downloading executables..."
    for exe in "${executables[@]}"; do
        download_file "${DOWNLOAD_BASE}/${exe}"
    done
    for exe in "${executables[@]}"; do
        if [[ -f "$exe" ]]; then
            cp -f "$exe" "$METRICI_DIR/"
            chmod +x "$METRICI_DIR/$exe"
            rm -f "$exe"
        else
            log_warn "Executable not found: $exe"
        fi
    done

    # Object files
    local objects=("objw.one" "objw.two" "objw.three" "objw.four" "objw.five" \
        "objw.six" "objw.seven" "objw.eight" "objw.nine" "objw.ten" \
        "objw.eleven" "objw.twelve" "objw.thirteen" "objw.zero")
    log_info "Downloading object files..."
    for obj in "${objects[@]}"; do
        download_file "${DOWNLOAD_BASE}/${obj}"
    done
    for obj in "${objects[@]}"; do
        if [[ -f "$obj" ]]; then
            cp -f "$obj" "$METRICI_OBJECTS_DIR/"
            rm -f "$obj"
        else
            log_warn "Object file not found: $obj"
        fi
    done

    # Configuration and helpers
    local configs=("lv.ini" "laser.wav" "metrici-win.zip")
    log_info "Downloading config files..."
    for cfg in "${configs[@]}"; do
        download_file "${DOWNLOAD_BASE}/${cfg}"
    done
    for cfg in "${configs[@]}"; do
        if [[ -f "$cfg" ]]; then
            cp -f "$cfg" "$METRICI_DIR/"
            rm -f "$cfg"
        else
            log_warn "Config file not found: $cfg"
        fi
    done

    # Scripts
    local delete_scripts=("metrici-ac-delete_events_after.sh" "start-metrici.sh" \
        "shmclear.sh" "metrici-shmclear.sh" \
        "metrici-send_jobs.sh" "metrici-watchdog.sh" \
        "metrici-ccr-delete_events_after.sh" "metrici-eer-delete_events_after.sh" \
        "metrici-lc-delete_events_after.sh" "metrici-lpr-delete_events_after.sh" \
        "metrici-ppd-delete_events_after.sh" "metrici-qr-delete_events_after.sh" \
        "metrici-ta-delete_events_after.sh" "metrici-vj-delete_events_after.sh")
    log_info "Downloading delete scripts..."
    for script in "${delete_scripts[@]}"; do
        download_file "${DOWNLOAD_BASE}/${script}"
    done
    for script in "${delete_scripts[@]}"; do
        if [[ -f "$script" ]]; then
            cp -f "$script" "$METRICI_DIR/"
            chmod +x "$METRICI_DIR/$script"
            rm -f "$script"
        else
            log_warn "Script not found: $script"
        fi
    done

    # Check alarms scripts
    local alarm_scripts=("metrici-ac-check_alarms.sh" "metrici-lc-check_alarms.sh" \
        "metrici-lpr-check_alarms.sh" "metrici-ppd-check_alarms.sh" \
        "metrici-ta-check_alarms.sh")
    log_info "Downloading alarm scripts..."
    for script in "${alarm_scripts[@]}"; do
        download_file "${DOWNLOAD_BASE}/${script}"
    done
    for script in "${alarm_scripts[@]}"; do
        if [[ -f "$script" ]]; then
            cp -f "$script" "$METRICI_DIR/"
            chmod +x "$METRICI_DIR/$script"
            rm -f "$script"
        else
            log_warn "Alarm script not found: $script"
        fi
    done

    # Trigger URLs script
    download_file "${DOWNLOAD_BASE}/metrici-lpr-trigger_parking_urls.sh"
    if [[ -f "metrici-lpr-trigger_parking_urls.sh" ]]; then
        cp -f metrici-lpr-trigger_parking_urls.sh "$METRICI_DIR/"
        chmod +x "$METRICI_DIR/metrici-lpr-trigger_parking_urls.sh"
        rm -f metrici-lpr-trigger_parking_urls.sh
    fi

    log_success "Metrici files installed"
}

step_configure_autostart() {
    log_step "Configuring autostart"

    cat > start-metrici.sh.desktop << EOF
[Desktop Entry]
Type=Application
Exec=${METRICI_DIR}/start-metrici.sh
Hidden=false
NoDisplay=false
X-GNOME-AutostartEnabled=true
Name=metrici
Comment=
EOF

    mkdir -p "$HOME_DIR/.config/autostart"
    cp -f start-metrici.sh.desktop "$HOME_DIR/.config/autostart/"
    rm -f start-metrici.sh.desktop

    log_success "Autostart configured"
}

step_install_license_system() {
    log_step "Installing license system"

    download_file "${DOWNLOAD_BASE}/aksusbd-10.13.1.tar.gz"
    tar -xzf aksusbd-10.13.1.tar.gz
    cd aksusbd-10.13.1
    sudo ./dinst
    cd ..
    rm -rf aksusbd-10.13.1 aksusbd-10.13.1.tar.gz

    log_success "License system installed"
}

step_install_pylon() {
    log_step "Installing Pylon SDK"

    download_file "${DOWNLOAD_BASE}/pylon5.tar.gz"
    sudo mkdir -p /usr/local/lib/pylon
    sudo tar --strip-components=1 -xzf pylon5.tar.gz -C /usr/local/lib/pylon
    sudo ldconfig

    download_file "${DOWNLOAD_BASE}/pylon.conf"
    sudo cp -f pylon.conf /etc/ld.so.conf.d/
    sudo ldconfig

    rm -f pylon5.tar.gz pylon.conf

    log_success "Pylon SDK installed"
}

step_install_web_interface() {
    log_step "Installing web interface"

    download_file "${DOWNLOAD_BASE}/metrici3.webi.tar.gz"
    sudo tar --strip-components=4 -xzf metrici3.webi.tar.gz -C /var/www/html
    sudo chown -R www-data:www-data /var/www/html

    sudo mkdir -p /var/www/metrici_storage
    sudo chown www-data:www-data /var/www/metrici_storage
    sudo chmod 777 /var/www/metrici_storage

    sudo rm -f /var/www/html/index.html
    rm -f metrici3.webi.tar.gz

    sudo mkdir -p /ramdisk
    if ! grep -q "/ramdisk" /etc/fstab 2>/dev/null; then
        echo "tmpfs /ramdisk tmpfs size=512M,noatime 0 0" | sudo tee -a /etc/fstab >/dev/null
    fi
    sudo systemctl daemon-reload
    sudo mount -a

    log_success "Web interface installed"
}

step_install_php_dependencies() {
    log_step "Installing PHP dependencies"

    local php_deps=("mdba.tar.gz" "mdb2.tar.gz" "driver-mysqli.tar.gz" "PHPMailer.tar.gz")

    log_info "Downloading PHP dependencies..."
    for dep in "${php_deps[@]}"; do
        download_file "${DOWNLOAD_BASE}/${dep}"
    done

    # Extract if files exist
    if [[ -f "mdba.tar.gz" ]]; then
        log_info "Extracting mdba.tar.gz..."
        sudo mkdir -p /usr/share/php
        sudo tar --strip-components=1 -xzf mdba.tar.gz -C /usr/share/php
        rm -f mdba.tar.gz
    fi

    if [[ -f "mdb2.tar.gz" ]]; then
        log_info "Extracting mdb2.tar.gz..."
        sudo mkdir -p /usr/share/php/MDB2
        sudo tar --strip-components=1 -xzf mdb2.tar.gz -C /usr/share/php/MDB2
        rm -f mdb2.tar.gz
    fi

    if [[ -f "driver-mysqli.tar.gz" ]]; then
        log_info "Extracting driver-mysqli.tar.gz..."
        sudo mkdir -p /usr/share/php
        sudo tar --strip-components=3 -xzf driver-mysqli.tar.gz -C /usr/share/php
        rm -f driver-mysqli.tar.gz
    fi

    if [[ -f "PHPMailer.tar.gz" ]]; then
        log_info "Extracting PHPMailer.tar.gz..."
        sudo mkdir -p /usr/share/php
        sudo tar -xzf PHPMailer.tar.gz -C /usr/share/php
        rm -f PHPMailer.tar.gz
    fi

    log_success "PHP dependencies installed"
}

step_install_cron_jobs() {
    log_step "Installing cron jobs"

    local cron_files=("cron-hourly-general.sh" "cron-daily-general.sh" \
        "cron-weekly-general.sh" "cron-monthly-general.sh" \
        "cron-hourly-ac.sh" "cron-hourly-lc.sh" "cron-hourly-ppd.sh")

    for file in "${cron_files[@]}"; do
        download_file "${DOWNLOAD_BASE}/${file}"
    done

    for file in "${cron_files[@]}"; do
        if [[ -f "$file" ]]; then
            chmod +x "$file"
        fi
    done

    sudo cp -f cron-hourly-general.sh /etc/cron.hourly/
    sudo cp -f cron-daily-general.sh /etc/cron.daily/
    sudo cp -f cron-weekly-general.sh /etc/cron.weekly/
    sudo cp -f cron-monthly-general.sh /etc/cron.monthly/
    sudo cp -f cron-hourly-ac.sh /etc/cron.hourly/
    sudo cp -f cron-hourly-lc.sh /etc/cron.hourly/
    sudo cp -f cron-hourly-ppd.sh /etc/cron.hourly/

    rm -f "${cron_files[@]}"

    log_success "Cron jobs installed"
}

step_install_teamviewer() {
    log_step "Installing TeamViewer"

    download_file "https://download.teamviewer.com/download/linux/teamviewer_amd64.deb"
    sudo apt-get install -y ./teamviewer_amd64.deb
    rm -f teamviewer_amd64.deb

    log_success "TeamViewer installed"
}

step_install_rustdesk() {
    log_step "Installing RustDesk"

    download_file "https://github.com/rustdesk/rustdesk/releases/download/1.4.9/rustdesk-1.4.9-x86_64.deb"
    sudo apt-get install -y ./rustdesk-1.4.9-x86_64.deb
    rm -f rustdesk-1.4.9-x86_64.deb
    sudo rustdesk --option custom-rendezvous-server rustdesk.metrici.ro
    sudo rustdesk --option key "RRT4NS6QQwoSsUbQKJBWBPdIKw37pibT0tC0JMtaWAE="

    log_success "RustDesk installed"
}

step_configure_firewall() {
    log_step "Configuring firewall"
    sudo systemctl stop ufw
    sudo systemctl disable ufw
    log_success "Firewall disabled"
}

step_final_update() {
    log_step "Final system update"
    sudo apt update -y
    sudo apt upgrade -y
    log_success "System updated"
}

# =============================================================================
# Summary Report
# =============================================================================

generate_summary_report() {
    local report_file="${SCRIPT_DIR}/install-report.txt"
    local log_file="${SCRIPT_DIR}/install.log"

    echo "" >&4
    echo -e "${BOLD}==========================================${NC}" >&4
    echo -e "${BOLD}     Metrici Installation Summary${NC}" >&4
    echo -e "${BOLD}==========================================${NC}" >&4
    echo "" >&4

    local success_count=0
    local failed_count=0
    local skipped_count=0
    local total_count=${#STEP_ORDER[@]}

    for step in "${STEP_ORDER[@]}"; do
        local status="${STEP_STATUS[$step]:-unknown}"
        local critical="${STEP_CRITICAL[$step]:-false}"
        local critical_tag=""
        local display_name
        display_name=$(step_display_name "$step")
        [[ "$critical" == "true" ]] && critical_tag=" [CRITICAL]"

        case "$status" in
            success)
                echo -e "  ${GREEN}[✓]${NC} $display_name${critical_tag}" >&4
                success_count=$((success_count + 1))
                ;;
            failed)
                echo -e "  ${RED}[✗]${NC} $display_name${critical_tag}" >&4
                failed_count=$((failed_count + 1))
                ;;
            skipped)
                echo -e "  ${YELLOW}[−]${NC} $display_name${critical_tag} (skipped - already done)" >&4
                skipped_count=$((skipped_count + 1))
                ;;
            *)
                echo -e "  ${RED}[?]${NC} $display_name${critical_tag} (status: $status)" >&4
                failed_count=$((failed_count + 1))
                ;;
        esac
    done

    echo "" >&4
    echo -e "${BOLD}Results:${NC}" >&4
    echo -e "  Total:   $total_count" >&4
    echo -e "  ${GREEN}Success: $success_count${NC}" >&4
    echo -e "  ${RED}Failed:  $failed_count${NC}" >&4
    echo -e "  ${YELLOW}Skipped: $skipped_count${NC}" >&4
    echo "" >&4

    # Also write to file
    {
        echo "=========================================="
        echo "Metrici Installation Report"
        echo "=========================================="
        echo "Date: $(date '+%Y-%m-%d %H:%M:%S')"
        echo "Hostname: $(hostname)"
        echo "User: $(whoami)"
        echo "System: $(uname -a)"
        echo ""
        echo "Configuration:"
        echo "  - Metrici Directory: ${METRICI_DIR}"
        echo "  - Database: ${METRICI_DB_NAME}"
        echo "  - Timezone: ${TIMEZONE}"
        echo ""
        echo "Installed Components:"
        echo "  - GPU Drivers: $(lspci | grep -E 'VGA|3D' | head -1 || echo 'Not detected')"
        echo "  - Apache: $(apache2 -v 2>/dev/null | grep 'Apache' || echo 'Not installed')"
        echo "  - PHP: $(php -v 2>/dev/null | head -1 || echo 'Not installed')"
        echo "  - MySQL: $(mysql --version 2>/dev/null || echo 'Not installed')"
        echo "  - OpenCV: $(pkg-config --modversion opencv4 2>/dev/null || echo 'Not installed')"
        echo ""
        echo "Steps:"
        for step in "${STEP_ORDER[@]}"; do
            local status="${STEP_STATUS[$step]:-unknown}"
            local display_name
            display_name=$(step_display_name "$step")
            echo "  - $display_name: $status"
        done
        echo ""
        echo "Results: $success_count/$total_count successful"
        echo "=========================================="
    } > "$report_file"

    # Also save detailed log
    cp "$STATE_FILE" "$log_file" 2>/dev/null || true

    log_info "Report saved to: $report_file"
    log_info "State saved to: $STATE_FILE"

    if [[ $failed_count -gt 0 ]]; then
        echo "" >&4
        echo -e "${RED}${BOLD}Some steps failed!${NC}" >&4
        echo -e "Use ${BOLD}${SCRIPT_NAME} --resume${NC} to retry failed steps" >&4
        return 1
    fi

    echo "" >&4
    echo -e "${GREEN}${BOLD}==========================================${NC}" >&4
    echo -e "${GREEN}${BOLD}  Installation completed successfully!${NC}" >&4
    echo -e "${GREEN}${BOLD}==========================================${NC}" >&4
    echo "" >&4
    echo -e "Please ${BOLD}reboot${NC} the system." >&4
    echo "" >&4
    return 0
}

# =============================================================================
# Execute Single Step
# =============================================================================

execute_step() {
    local step_name="$1"
    local step_num="$2"
    local display_name
    display_name=$(step_display_name "$step_name")

    CURRENT_STEP=$step_num

    # Start spinner showing current step
    start_spinner "$display_name"

    # Save terminal fd 4 to fd 5, then redirect fd 4 to verbose log
    # During step execution, ALL output (including log_* messages) goes to log
    # Only the spinner remains visible on terminal
    exec 5>&4
    exec 4>>"$VERBOSE_LOG"

    local step_result=0
    "$step_name" >> "$VERBOSE_LOG" 2>&1 || step_result=$?

    # Restore fd 4 to terminal
    exec 4>&5
    exec 5>&-

    # Stop spinner
    stop_spinner

    if [[ $step_result -ne 0 ]]; then
        update_step_status "$step_name" "failed"

        if is_step_critical "$step_name"; then
            FAILED_CRITICAL="$display_name"
            show_step_result "$step_num" "$display_name" "$TOTAL_STEPS" "failed"
            close_progress_bar
            return 1
        else
            show_step_result "$step_num" "$display_name" "$TOTAL_STEPS" "failed"
            log_warn "Non-critical step failed, continuing: $display_name"
        fi
    else
        update_step_status "$step_name" "success"
        show_step_result "$step_num" "$display_name" "$TOTAL_STEPS" "success"
    fi

    return 0
}

# =============================================================================
# Main
# =============================================================================

main() {
    # Set up file descriptors:
    # fd 4 = terminal (for our log messages)
    # stdout/stderr will be redirected to log file during steps
    exec 4>&1

    # Initialize verbose log
    : > "$VERBOSE_LOG"

    # Parse arguments
    local resume=false
    for arg in "$@"; do
        case "$arg" in
            --resume) resume=true ;;
            --help)
                echo "Usage: $0 [OPTIONS]" >&4
                echo "" >&4
                echo "Options:" >&4
                echo "  --resume  Resume installation from last successful step" >&4
                echo "  --help    Show this help message" >&4
                exit 0
                ;;
            *)
                log_error "Unknown option: $arg"
                echo "Use --help for usage information" >&4
                exit 1
                ;;
        esac
    done

    # Display header
    echo "" >&4
    echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════╗${NC}" >&4
    echo -e "${BOLD}${CYAN}║   Metrici Ubuntu 24.04 Installer v4.0    ║${NC}" >&4
    echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════╝${NC}" >&4
    echo "" >&4

    # Detect if running as root
    if [[ "$(id -u)" -eq 0 ]]; then
        IS_ROOT=true
        log_info "Running as root, sudo not required"
        # Override sudo to be a pass-through when running as root
        sudo() {
            "$@"
        }
    else
        IS_ROOT=false
        log_info "Running as user $(whoami), sudo will be used for privileged operations"
        log_info "Requesting sudo credentials..."
        sudo -v

        # Start sudo keepalive
        while true; do
            sudo -n true 2>/dev/null
            sleep 60
            kill -0 $$ 2>/dev/null || exit
        done &
        SUDO_KEEPALIVE_PID=$!
    fi

    # Initialize
    init_state_file
    load_state
    INSTALL_START_TIME="$(date -Iseconds)"

    # Register all steps (name, critical?)
    register_step "step_validate_dependencies" "true"
    register_step "step_configure_gpu_drivers" "false"
    register_step "step_setup_system_packages" "true"
    register_step "step_configure_php" "true"
    register_step "step_configure_apache" "true"
    register_step "step_configure_mysql" "true"
    register_step "step_configure_memcached" "false"
    register_step "step_install_opencv" "true"
    register_step "step_install_metrici_files" "true"
    register_step "step_configure_autostart" "false"
    register_step "step_install_license_system" "true"
    register_step "step_install_pylon" "true"
    register_step "step_install_web_interface" "true"
    register_step "step_install_php_dependencies" "true"
    register_step "step_install_cron_jobs" "false"
    register_step "step_install_teamviewer" "false"
    register_step "step_install_rustdesk" "false"
    register_step "step_configure_firewall" "false"
    register_step "step_final_update" "false"

    TOTAL_STEPS=${#STEP_ORDER[@]}

    # Display plan
    log_info "Total steps: $TOTAL_STEPS"
    if [[ "$resume" == "true" ]]; then
        log_info "Resuming from last successful step"
    fi
    echo "" >&4

    # Initialize progress bar
    init_progress_bar

    # Execute steps
    local step_num=0
    for step_name in "${STEP_ORDER[@]}"; do
        step_num=$((step_num + 1))

        # Check if we can skip (already succeeded)
        if should_skip_step "$step_name"; then
            local display_name
            display_name=$(step_display_name "$step_name")
            show_step_result "$step_num" "$display_name" "$TOTAL_STEPS" "skipped"
            update_step_status "$step_name" "skipped"
            continue
        fi

        # Execute step
        if ! execute_step "$step_name" "$step_num"; then
            # Critical step failed
            break
        fi
    done

    # Close progress bar
    close_progress_bar
    echo ""

    # Write final state file
    write_state_file "$INSTALL_START_TIME" "$(date -Iseconds)"

    # Generate report
    generate_summary_report
}

main "$@"
