#!/usr/bin/env bash
# gniza4cp — cPanel Backup, Restore & Disaster Recovery
# CLI entrypoint and command routing

set -euo pipefail
umask 077

# Resolve install directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -L "${BASH_SOURCE[0]}" ]]; then
    SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
fi
BASE_DIR="$(dirname "$SCRIPT_DIR")"
LIB_DIR="$BASE_DIR/lib"

# Source libraries
source "$LIB_DIR/constants.sh"
source "$LIB_DIR/utils.sh"
source "$LIB_DIR/logging.sh"
source "$LIB_DIR/config.sh"
source "$LIB_DIR/locking.sh"
source "$LIB_DIR/ssh.sh"
source "$LIB_DIR/accounts.sh"
source "$LIB_DIR/pkgacct.sh"
source "$LIB_DIR/snapshot.sh"
source "$LIB_DIR/transfer.sh"
source "$LIB_DIR/retention.sh"
source "$LIB_DIR/verify.sh"
source "$LIB_DIR/notify.sh"
source "$LIB_DIR/restore.sh"
source "$LIB_DIR/remotes.sh"
source "$LIB_DIR/rclone.sh"
source "$LIB_DIR/schedule.sh"
source "$LIB_DIR/sysbackup.sh"
source "$LIB_DIR/sysrestore.sh"

# ── Argument parsing helpers ───────────────────────────────────

get_opt() {
    local name="$1"; shift
    for arg in "$@"; do
        case "$arg" in
            --${name}=*) echo "${arg#*=}"; return 0 ;;
        esac
    done
    return 1
}

has_flag() {
    local name="$1"; shift
    for arg in "$@"; do
        [[ "$arg" == "--$name" ]] && return 0
    done
    return 1
}

# ── Commands ───────────────────────────────────────────────────

# Transfer + finalize + retention for a single account on the current remote.
# Globals REMOTE_* must already be set via load_remote().
# Returns 0 on success, 1 on failure.
_backup_to_current_remote() {
    local user="$1"
    local ts="$2"
    local remote_label="$CURRENT_REMOTE_NAME"

    # Clean any leftover partials
    clean_partial_snapshots "$user"

    # Find previous snapshot for hardlinking
    local prev; prev=$(get_latest_snapshot "$user") || prev=""

    # Transfer pkgacct data
    if ! transfer_pkgacct "$user" "$ts" "$prev"; then
        log_error "[$remote_label] Transfer pkgacct failed for $user"
        return 1
    fi

    # Transfer homedir
    if ! transfer_homedir "$user" "$ts" "$prev"; then
        log_error "[$remote_label] Transfer homedir failed for $user"
        return 1
    fi

    # Finalize snapshot
    if ! finalize_snapshot "$user" "$ts"; then
        log_error "[$remote_label] Finalize failed for $user"
        return 1
    fi

    # Enforce retention
    enforce_retention "$user"
    return 0
}

cmd_backup() {
    require_root
    require_cmd rsync
    require_cmd ssh
    require_cmd hostname
    require_cmd gzip
    local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
    load_config "$config_file"
    validate_config || die "Invalid configuration"
    init_logging

    local dry_run=false
    has_flag dry-run "$@" && dry_run=true

    local run_sysbackup=false
    has_flag sysbackup "$@" && run_sysbackup=true

    local skip_suspended=false
    has_flag skip-suspended "$@" && skip_suspended=true

    local single_account=""
    single_account=$(get_opt account "$@" 2>/dev/null) || true

    local remote_flag=""
    remote_flag=$(get_opt remote "$@" 2>/dev/null) || true

    acquire_lock
    trap 'cleanup_all_temp; release_lock' EXIT

    # Resolve target remotes
    local remotes=""
    remotes=$(get_target_remotes "$remote_flag") || die "Invalid remote specification"

    # Save original globals (will be restored after each load_remote)
    _save_remote_globals

    # Test connectivity to all targets upfront
    while IFS= read -r rname; do
        [[ -z "$rname" ]] && continue
        load_remote "$rname" || die "Failed to load remote: $rname"
        if _is_rclone_mode; then
            test_rclone_connection || die "Cannot connect to remote '$rname' (${REMOTE_TYPE})"
        else
            test_ssh_connection || die "Cannot connect to remote '$rname' (${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT})"
        fi
    done <<< "$remotes"
    _restore_remote_globals

    local start_time; start_time=$(date +%s)
    local ts; ts=$(timestamp)

    # Determine accounts
    local accounts
    if [[ -n "$single_account" ]]; then
        account_exists "$single_account" || die "Account does not exist: $single_account"
        accounts="$single_account"
    else
        accounts=$(get_backup_accounts "$skip_suspended")
    fi

    if [[ -z "$accounts" ]]; then
        die "No accounts found to backup"
    fi

    local total=0 succeeded=0 failed=0
    local failed_accounts=""

    while IFS= read -r user; do
        [[ -z "$user" ]] && continue
        ((total++)) || true

        log_info "=== Backing up account: $user ($total) ==="

        if [[ "$dry_run" == "true" ]]; then
            log_info "[DRY RUN] Would backup account: $user"
            log_info "[DRY RUN]   pkgacct --nocomp --skiphomedir -> temp"
            log_info "[DRY RUN]   gzip SQL files"
            while IFS= read -r rname; do
                [[ -z "$rname" ]] && continue
                load_remote "$rname"
                local prev; prev=$(get_latest_snapshot "$user") || prev=""
                log_info "[DRY RUN]   [$rname] rsync pkgacct to ${REMOTE_HOST} (link-dest: ${prev:-none})"
                log_info "[DRY RUN]   [$rname] rsync homedir to ${REMOTE_HOST} (link-dest: ${prev:-none})"
                log_info "[DRY RUN]   [$rname] finalize snapshot: $ts"
                log_info "[DRY RUN]   [$rname] enforce retention: keep $RETENTION_COUNT"
            done <<< "$remotes"
            _restore_remote_globals
            ((succeeded++)) || true
            continue
        fi

        # Run pkgacct ONCE
        if ! run_pkgacct "$user"; then
            log_error "Backup failed for $user: pkgacct error"
            ((failed++)) || true
            failed_accounts+="  - $user (pkgacct failed)"$'\n'
            cleanup_pkgacct "$user"
            continue
        fi

        # Gzip SQL files ONCE
        if ! gzip_sql_files "$user"; then
            log_warn "SQL gzip had issues for $user, continuing..."
        fi

        # Transfer to each remote
        local user_failed=false
        while IFS= read -r rname; do
            [[ -z "$rname" ]] && continue
            load_remote "$rname"
            log_info "--- Transferring $user to remote '$rname' ---"
            if ! _backup_to_current_remote "$user" "$ts"; then
                log_error "Backup to remote '$rname' failed for $user"
                failed_accounts+="  - $user ($rname: transfer failed)"$'\n'
                user_failed=true
            fi
        done <<< "$remotes"
        _restore_remote_globals

        if [[ "$user_failed" == "true" ]]; then
            ((failed++)) || true
        else
            ((succeeded++)) || true
            log_info "Backup completed for $user (all remotes)"
        fi

        # Cleanup local temp
        cleanup_pkgacct "$user"
    done <<< "$accounts"

    local end_time; end_time=$(date +%s)
    local duration=$(( end_time - start_time ))

    # Print summary
    echo ""
    echo "============================================"
    echo "Backup Summary"
    echo "============================================"
    echo "Timestamp:   $ts"
    echo "Duration:    $(human_duration $duration)"
    echo "Remotes:     $(echo "$remotes" | tr '\n' ' ')"
    echo "Total:       $total"
    echo "Succeeded:   ${C_GREEN}${succeeded}${C_RESET}"
    if (( failed > 0 )); then
        echo "Failed:      ${C_RED}${failed}${C_RESET}"
        echo ""
        echo "Failed accounts:"
        echo "$failed_accounts"
    else
        echo "Failed:      0"
    fi
    echo "============================================"

    # Run system backup if --sysbackup was requested
    if [[ "$run_sysbackup" == "true" ]]; then
        echo ""
        log_info "=== Running system backup (--sysbackup) ==="
        # Release lock so sysbackup can acquire its own
        release_lock
        local sysbackup_args=()
        [[ -n "$remote_flag" ]] && sysbackup_args+=(--remote="$remote_flag")
        [[ "$dry_run" == "true" ]] && sysbackup_args+=(--dry-run)
        # Run as subprocess so its exit doesn't kill our process
        /usr/local/bin/gniza4cp sysbackup "${sysbackup_args[@]}" || log_error "System backup failed"
        acquire_lock
    fi

    # Send notification
    send_backup_report "$total" "$succeeded" "$failed" "$duration" "$failed_accounts"

    if (( failed > 0 && succeeded > 0 )); then
        exit "$EXIT_PARTIAL"
    elif (( failed > 0 )); then
        exit "$EXIT_FATAL"
    fi
    exit "$EXIT_OK"
}

# Helper: load remote context for restore commands.
# --remote=NAME is always required for restore.
_restore_load_remote() {
    local remote_flag="$1"
    [[ -z "$remote_flag" ]] && die "Specify --remote=NAME for restore."$'\n'"Available remotes: $(list_remotes | tr '\n' ' ')"
    _save_remote_globals
    load_remote "$remote_flag" || die "Failed to load remote: $remote_flag"
}

# Helper: test connection to current remote (SSH or rclone).
_test_connection() {
    if _is_rclone_mode; then
        test_rclone_connection || die "Cannot connect to remote (${REMOTE_TYPE})"
    else
        test_ssh_connection || die "Cannot connect to remote server"
    fi
}

cmd_restore() {
    require_root
    require_cmd rsync
    require_cmd ssh
    require_cmd hostname
    local subcommand="${1:-}"
    shift 2>/dev/null || true

    case "$subcommand" in
        account)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore account <name> [--remote=NAME] [--timestamp=TS] [--terminate]"
            validate_account_name "$name" || die "Invalid account name"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""
            [[ -n "$timestamp" ]] && { validate_timestamp "$timestamp" || die "Invalid timestamp"; }
            local exclude; exclude=$(get_opt exclude "$@" 2>/dev/null) || exclude=""
            local terminate_val; terminate_val=$(get_opt terminate "$@" 2>/dev/null) || terminate_val=""
            local terminate=false
            [[ "$terminate_val" == "1" ]] && terminate=true

            _test_connection
            restore_full_account "$name" "$timestamp" "$terminate" "$exclude"
            ;;
        files)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore files <name> [--remote=NAME] [--path=subpath] [--timestamp=TS]"
            validate_account_name "$name" || die "Invalid account name"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local subpath; subpath=$(get_opt path "$@" 2>/dev/null) || subpath=""
            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""
            [[ -n "$timestamp" ]] && { validate_timestamp "$timestamp" || die "Invalid timestamp"; }
            local exclude; exclude=$(get_opt exclude "$@" 2>/dev/null) || exclude=""

            _test_connection
            restore_files "$name" "$subpath" "$timestamp" "$exclude"
            ;;
        database)
            local name="${1:-}"
            local dbname="${2:-}"
            [[ -z "$name" ]] && die "Usage: gniza4cp restore database <name> [<dbname>] [--remote=NAME] [--timestamp=TS]"
            validate_account_name "$name" || die "Invalid account name"
            shift 2>/dev/null || true
            # If dbname looks like a flag, it's not a dbname
            if [[ -n "$dbname" && "$dbname" != --* ]]; then
                shift 2>/dev/null || true
            else
                dbname=""
            fi

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""
            [[ -n "$timestamp" ]] && { validate_timestamp "$timestamp" || die "Invalid timestamp"; }

            _test_connection
            if [[ -n "$dbname" ]]; then
                restore_database "$name" "$dbname" "$timestamp"
            else
                restore_all_databases "$name" "$timestamp"
            fi
            ;;
        mailbox)
            local name="${1:-}"
            local email="${2:-}"
            [[ -z "$name" ]] && die "Usage: gniza4cp restore mailbox <name> [<email@domain>] [--remote=NAME] [--timestamp=TS]"
            validate_account_name "$name" || die "Invalid account name"
            shift 2>/dev/null || true
            # If email looks like a flag, it's not an email
            if [[ -n "$email" && "$email" != --* ]]; then
                shift 2>/dev/null || true
            else
                email=""
            fi

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""
            [[ -n "$timestamp" ]] && { validate_timestamp "$timestamp" || die "Invalid timestamp"; }

            _test_connection
            if [[ -n "$email" ]]; then
                restore_mailbox "$name" "$email" "$timestamp"
            else
                restore_all_mailboxes "$name" "$timestamp"
            fi
            ;;
        list-databases)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore list-databases <name> [--remote=NAME] [--timestamp=TS]"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            list_snapshot_databases "$name" "$timestamp"
            ;;
        list-mailboxes)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore list-mailboxes <name> [--remote=NAME] [--timestamp=TS]"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            list_snapshot_mailboxes "$name" "$timestamp"
            ;;
        list-files)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore list-files <name> [--remote=NAME] [--timestamp=TS] [--path=subdir]"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""
            local subpath; subpath=$(get_opt path "$@" 2>/dev/null) || subpath=""

            _test_connection
            list_snapshot_files "$name" "$timestamp" "$subpath"
            ;;
        list-dbusers)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore list-dbusers <name> [--remote=NAME] [--timestamp=TS]"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            list_snapshot_dbusers "$name" "$timestamp"
            ;;
        list-cron)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore list-cron <name> [--remote=NAME] [--timestamp=TS]"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            list_snapshot_cron "$name" "$timestamp"
            ;;
        list-dns)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore list-dns <name> [--remote=NAME] [--timestamp=TS]"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            list_snapshot_dns "$name" "$timestamp"
            ;;
        list-ssl)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore list-ssl <name> [--remote=NAME] [--timestamp=TS]"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            list_snapshot_ssl "$name" "$timestamp"
            ;;
        cron)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore cron <name> [--remote=NAME] [--timestamp=TS]"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            restore_cron "$name" "$timestamp"
            ;;
        dbusers)
            local name="${1:-}"
            local specific_dbuser="${2:-}"
            [[ -z "$name" ]] && die "Usage: gniza4cp restore dbusers <name> [<dbuser>] [--remote=NAME] [--timestamp=TS]"
            shift 2>/dev/null || true
            if [[ -n "$specific_dbuser" && "$specific_dbuser" != --* ]]; then
                shift 2>/dev/null || true
            else
                specific_dbuser=""
            fi

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            restore_dbusers "$name" "$specific_dbuser" "$timestamp"
            ;;
        cpconfig)
            local name="${1:-}"
            shift 2>/dev/null || true
            [[ -z "$name" ]] && die "Usage: gniza4cp restore cpconfig <name> [--remote=NAME] [--timestamp=TS]"

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            restore_cpconfig "$name" "$timestamp"
            ;;
        domains)
            local name="${1:-}"
            local specific_domain="${2:-}"
            [[ -z "$name" ]] && die "Usage: gniza4cp restore domains <name> [<domain>] [--remote=NAME] [--timestamp=TS]"
            shift 2>/dev/null || true
            if [[ -n "$specific_domain" && "$specific_domain" != --* ]]; then
                shift 2>/dev/null || true
            else
                specific_domain=""
            fi

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            restore_domains "$name" "$specific_domain" "$timestamp"
            ;;
        ssl)
            local name="${1:-}"
            local specific_cert="${2:-}"
            [[ -z "$name" ]] && die "Usage: gniza4cp restore ssl <name> [<cert>] [--remote=NAME] [--timestamp=TS]"
            shift 2>/dev/null || true
            if [[ -n "$specific_cert" && "$specific_cert" != --* ]]; then
                shift 2>/dev/null || true
            else
                specific_cert=""
            fi

            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            restore_ssl "$name" "$specific_cert" "$timestamp"
            ;;
        server)
            local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            validate_config || die "Invalid configuration"
            init_logging

            local remote_flag; remote_flag=$(get_opt remote "$@" 2>/dev/null) || remote_flag=""
            _restore_load_remote "$remote_flag"

            local timestamp; timestamp=$(get_opt timestamp "$@" 2>/dev/null) || timestamp=""

            _test_connection
            restore_server "$timestamp"
            ;;
        *)
            die "Unknown restore subcommand: $subcommand"$'\n'"Usage: gniza4cp restore {account|files|database|mailbox|cron|dbusers|cpconfig|domains|ssl|list-databases|list-mailboxes|list-files|list-dbusers|list-cron|list-dns|list-ssl|server}"
            ;;
    esac
}

# Display listing for the current remote context (globals already set).
_list_current_remote() {
    local single_account="$1"
    local hostname; hostname=$(hostname -f)

    if [[ -n "$single_account" ]]; then
        local snapshots; snapshots=$(list_remote_snapshots "$single_account")
        if [[ -z "$snapshots" ]]; then
            echo "  (none)"
        else
            while IFS= read -r snap; do
                local snap_dir; snap_dir=$(get_snapshot_dir "$single_account")
                local size; size=$(remote_exec "du -sb '$snap_dir/$snap' 2>/dev/null | cut -f1" 2>/dev/null)
                printf "  %-25s %s\n" "$snap" "$(human_size "${size:-0}")"
            done <<< "$snapshots"
        fi
    else
        local accounts; accounts=$(list_remote_accounts)
        if [[ -z "$accounts" ]]; then
            echo "  (no backups)"
            return 0
        fi

        printf "  %-20s %-10s %-25s\n" "ACCOUNT" "SNAPSHOTS" "LATEST"
        printf "  %-20s %-10s %-25s\n" "-------" "---------" "------"

        while IFS= read -r user; do
            [[ -z "$user" ]] && continue
            local count; count=$(list_remote_snapshots "$user" | wc -l)
            local latest; latest=$(get_latest_snapshot "$user")
            printf "  %-20s %-10s %-25s\n" "$user" "$count" "${latest:-(none)}"
        done <<< "$accounts"
    fi
}

cmd_list() {
    local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
    load_config "$config_file"
    validate_config || die "Invalid configuration"
    init_logging

    # list accounts subcommand
    if [[ "${1:-}" == "accounts" ]]; then
        shift
        local remote_flag=""
        remote_flag=$(get_opt remote "$@" 2>/dev/null) || true
        [[ -z "$remote_flag" ]] && die "Usage: gniza4cp list accounts --remote=NAME"
        local remotes; remotes=$(get_target_remotes "$remote_flag") || die "Invalid remote"
        local rname; rname=$(head -1 <<< "$remotes")
        _save_remote_globals
        load_remote "$rname" || die "Failed to load remote: $rname"
        _test_connection || die "Connection failed to remote: $rname"
        list_remote_accounts
        _restore_remote_globals
        return 0
    fi

    local single_account=""
    single_account=$(get_opt account "$@" 2>/dev/null) || true

    local remote_flag=""
    remote_flag=$(get_opt remote "$@" 2>/dev/null) || true

    local remotes=""
    remotes=$(get_target_remotes "$remote_flag") || die "Invalid remote specification"

    local hostname; hostname=$(hostname -f)

    _save_remote_globals
    while IFS= read -r rname; do
        [[ -z "$rname" ]] && continue
        load_remote "$rname" || { log_error "Failed to load remote: $rname"; continue; }

        echo ""
        if [[ -n "$single_account" ]]; then
            echo "Snapshots for ${C_BOLD}$single_account${C_RESET} on ${C_BOLD}[$rname]${C_RESET} (${REMOTE_HOST}):"
        else
            echo "Backups on ${C_BOLD}[$rname]${C_RESET} (${REMOTE_HOST}):"
        fi
        echo ""
        local conn_ok=false
        if _is_rclone_mode; then
            test_rclone_connection 2>/dev/null && conn_ok=true
        else
            test_ssh_connection 2>/dev/null && conn_ok=true
        fi
        if [[ "$conn_ok" == "true" ]]; then
            _list_current_remote "$single_account"
        else
            echo "  ${C_RED}Connection failed${C_RESET}"
        fi
    done <<< "$remotes"
    _restore_remote_globals
}

cmd_verify() {
    local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
    load_config "$config_file"
    validate_config || die "Invalid configuration"
    init_logging

    local single_account=""
    single_account=$(get_opt account "$@" 2>/dev/null) || true

    local remote_flag=""
    remote_flag=$(get_opt remote "$@" 2>/dev/null) || true

    local remotes=""
    remotes=$(get_target_remotes "$remote_flag") || die "Invalid remote specification"

    _save_remote_globals
    local any_failed=false
    while IFS= read -r rname; do
        [[ -z "$rname" ]] && continue
        load_remote "$rname" || { log_error "Failed to load remote: $rname"; any_failed=true; continue; }

        echo ""
        echo "${C_BOLD}[$rname]${C_RESET} (${REMOTE_TYPE:-ssh}):"
        local conn_ok=false
        if _is_rclone_mode; then
            test_rclone_connection 2>/dev/null && conn_ok=true
        else
            test_ssh_connection 2>/dev/null && conn_ok=true
        fi
        if [[ "$conn_ok" == "true" ]]; then
            if [[ -n "$single_account" ]]; then
                verify_account_backup "$single_account" || any_failed=true
            else
                verify_all_accounts || any_failed=true
            fi
        else
            echo "  ${C_RED}Connection failed${C_RESET}"
            any_failed=true
        fi
    done <<< "$remotes"
    _restore_remote_globals
    [[ "$any_failed" == "true" ]] && return 1
}

_status_ssh_and_disk() {
    if _is_rclone_mode; then
        echo -n "  Connection:     "
        if test_rclone_connection 2>/dev/null; then
            echo "${C_GREEN}OK${C_RESET} (${REMOTE_TYPE})"
            echo -n "  Remote storage: "
            local size_json; size_json=$(_rclone_cmd about --json "$(_rclone_remote_path "")" 2>/dev/null) || true
            if [[ -n "$size_json" ]]; then
                local used; used=$(echo "$size_json" | grep -oP '"used":\s*\K[0-9]+' || echo "")
                local total; total=$(echo "$size_json" | grep -oP '"total":\s*\K[0-9]+' || echo "")
                if [[ -n "$used" && -n "$total" && "$total" -gt 0 ]]; then
                    echo "$(human_size "$used") used of $(human_size "$total")"
                elif [[ -n "$used" ]]; then
                    echo "$(human_size "$used") used"
                else
                    echo "available"
                fi
            else
                echo "unknown"
            fi
        else
            echo "${C_RED}FAILED${C_RESET}"
        fi
    else
        echo -n "  SSH connection: "
        if test_ssh_connection 2>/dev/null; then
            echo "${C_GREEN}OK${C_RESET}"
            echo -n "  Remote disk:    "
            local disk_info; disk_info=$(remote_exec "df -h '${REMOTE_BASE}' 2>/dev/null | tail -1" 2>/dev/null)
            if [[ -n "$disk_info" ]]; then
                echo "$disk_info" | awk '{printf "%s used of %s (%s)\n", $3, $2, $5}'
            else
                echo "unknown"
            fi
        else
            echo "${C_RED}FAILED${C_RESET}"
        fi
    fi
}

cmd_status() {
    local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
    load_config "$config_file"
    validate_config || die "Invalid configuration"

    local hostname; hostname=$(hostname -f)

    echo "${C_BOLD}gniza4cp v${GNIZA4CP_VERSION}${C_RESET}"
    echo ""
    echo "Hostname:       $hostname"
    echo "Log level:      ${LOG_LEVEL}"
    echo "Notifications:  ${NOTIFY_ON}${NOTIFY_EMAIL:+ ($NOTIFY_EMAIL)}"

    echo "Mode:           multi-remote"
    echo ""

    if has_remotes; then
        _save_remote_globals
        local remotes; remotes=$(list_remotes)
        while IFS= read -r rname; do
            [[ -z "$rname" ]] && continue
            load_remote "$rname" 2>/dev/null || { echo "  ${C_RED}Failed to load${C_RESET}"; continue; }
            echo "${C_BOLD}[$rname]${C_RESET} (${REMOTE_TYPE:-ssh})"
            if [[ "${REMOTE_TYPE:-ssh}" == "ssh" ]]; then
                echo "  Remote:         ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT}"
            elif [[ "$REMOTE_TYPE" == "s3" ]]; then
                echo "  Bucket:         ${S3_BUCKET}"
                echo "  Region:         ${S3_REGION}"
                [[ -n "${S3_ENDPOINT:-}" ]] && echo "  Endpoint:       ${S3_ENDPOINT}"
            elif [[ "$REMOTE_TYPE" == "gdrive" ]]; then
                echo "  Service acct:   ${GDRIVE_SERVICE_ACCOUNT_FILE}"
                [[ -n "${GDRIVE_ROOT_FOLDER_ID:-}" ]] && echo "  Root folder:    ${GDRIVE_ROOT_FOLDER_ID}"
            fi
            echo "  Remote base:    ${REMOTE_BASE}"
            echo "  Retention:      ${RETENTION_COUNT} snapshots"
            if (( BWLIMIT == 0 )); then echo "  Bandwidth:      unlimited"; else echo "  Bandwidth:      ${BWLIMIT} KB/s"; fi
            _status_ssh_and_disk
            echo ""
        done <<< "$remotes"
        _restore_remote_globals
    else
        echo "No remotes configured."
    fi

    echo ""

    # Check lock
    echo -n "Lock status:    "
    if [[ -f "${LOCK_FILE:-$DEFAULT_LOCK_FILE}" ]]; then
        local pid; pid=$(cat "${LOCK_FILE:-$DEFAULT_LOCK_FILE}" 2>/dev/null)
        if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
            echo "${C_YELLOW}LOCKED (PID $pid)${C_RESET}"
        else
            echo "stale lock file (no running process)"
        fi
    else
        echo "unlocked"
    fi

    # Last log
    local log_dir="${LOG_DIR:-$DEFAULT_LOG_DIR}"
    echo -n "Last log:       "
    local last_log; last_log=$(ls -1t "$log_dir"/gniza4cp-*.log 2>/dev/null | head -1)
    if [[ -n "$last_log" ]]; then
        echo "$(basename "$last_log")"
    else
        echo "(none)"
    fi
}

cmd_remote() {
    local subcommand="${1:-}"
    shift 2>/dev/null || true

    case "$subcommand" in
        list|ls|"")
            if ! has_remotes; then
                echo "No remotes configured."
                return 0
            fi

            local remotes; remotes=$(list_remotes)
            local count; count=$(echo "$remotes" | wc -l)

            echo "${C_BOLD}Configured remotes ($count):${C_RESET}"
            echo ""
            printf "  %-15s %-8s %-30s %s\n" "NAME" "TYPE" "DESTINATION" "BASE"
            printf "  %-15s %-8s %-30s %s\n" "----" "----" "-----------" "----"

            _save_remote_globals
            while IFS= read -r rname; do
                [[ -z "$rname" ]] && continue
                load_remote "$rname" 2>/dev/null || { printf "  %-15s %s\n" "$rname" "${C_RED}(failed to load)${C_RESET}"; continue; }

                local dest_str
                case "${REMOTE_TYPE:-ssh}" in
                    ssh)    dest_str="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT}" ;;
                    s3)     dest_str="s3://${S3_BUCKET}" ;;
                    gdrive) dest_str="gdrive:${GDRIVE_SERVICE_ACCOUNT_FILE##*/}" ;;
                esac
                printf "  %-15s %-8s %-30s %s\n" \
                    "$rname" "${REMOTE_TYPE:-ssh}" "$dest_str" "$REMOTE_BASE"
            done <<< "$remotes"
            _restore_remote_globals
            echo ""
            ;;
        delete|rm|remove)
            require_root
            local name="${1:-}"
            [[ -z "$name" ]] && die "Usage: gniza4cp remote delete <name>"

            local conf="$REMOTES_DIR/${name}.conf"
            if [[ ! -f "$conf" ]]; then
                die "Remote not found: $name (expected $conf)"
            fi

            # Show what will be deleted
            local config_file; config_file=$(get_opt config "${@:2}" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
            load_config "$config_file"
            load_remote "$name" 2>/dev/null
            echo "Remote:   $name"
            echo "Host:     ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT}"
            echo "Base:     ${REMOTE_BASE}"
            echo ""
            read -rp "Delete remote '$name'? [y/N] " answer
            [[ "$answer" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }

            rm -f "$conf"
            echo "Remote '$name' deleted."
            ;;
        *)
            die "Unknown remote subcommand: $subcommand"$'\n'"Usage: gniza4cp remote {list|delete <name>}"
            ;;
    esac
}

cmd_schedule() {
    require_root
    local subcommand="${1:-show}"
    shift 2>/dev/null || true

    local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
    load_config "$config_file"
    init_logging

    case "$subcommand" in
        add)
            local name="${1:-}"
            [[ -z "$name" ]] && die "Usage: gniza4cp schedule add <name>"
            _schedule_add "$name"
            ;;
        delete|rm|remove-schedule)
            local name="${1:-}"
            [[ -z "$name" ]] && die "Usage: gniza4cp schedule delete <name>"
            _schedule_delete "$name"
            ;;
        run)
            local name="${1:-}"
            [[ -z "$name" ]] && die "Usage: gniza4cp schedule run <name>"
            _schedule_run "$name"
            ;;
        list|ls)
            _schedule_list
            ;;
        install) install_schedules ;;
        show)    show_schedules ;;
        remove)  remove_schedules ;;
        *)       die "Unknown schedule subcommand: $subcommand"$'\n'"Usage: gniza4cp schedule {add|delete|run|list|install|show|remove}" ;;
    esac
}

_schedule_add() {
    local name="$1"

    if ! [[ "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
        die "Schedule name must be alphanumeric (hyphens and underscores allowed): $name"
    fi

    local config_file="$SCHEDULES_DIR/${name}.conf"

    if [[ -f "$config_file" ]]; then
        echo "Schedule config already exists: $config_file"
        read -rp "Overwrite? [y/N] " answer
        [[ "$answer" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }
    fi

    echo "${C_BOLD}gniza4cp schedule add${C_RESET} — New schedule: ${C_BOLD}$name${C_RESET}"
    echo ""

    echo "Schedule options: hourly, daily, weekly, monthly, custom"
    read -rp "Schedule type [daily]: " sched_type
    sched_type="${sched_type:-daily}"

    local sched_time="02:00"
    local sched_day=""
    local sched_cron=""

    read -rp "Schedule time HH:MM [02:00]: " sched_time
    sched_time="${sched_time:-02:00}"

    case "$sched_type" in
        weekly)
            read -rp "Day of week (0=Sun..6=Sat): " sched_day
            [[ -z "$sched_day" ]] && die "Day of week is required for weekly schedule"
            ;;
        monthly)
            read -rp "Day of month (1-28): " sched_day
            [[ -z "$sched_day" ]] && die "Day of month is required for monthly schedule"
            ;;
        custom)
            read -rp "Full cron expression (5 fields): " sched_cron
            [[ -z "$sched_cron" ]] && die "Cron expression is required for custom schedule"
            ;;
        hourly)
            read -rp "Hours between backups (1-23) [1]: " sched_day
            sched_day="${sched_day:-1}"
            ;;
        daily) ;;
        *) die "Invalid schedule type: $sched_type" ;;
    esac

    # Select remotes
    local sched_remotes=""
    if has_remotes; then
        echo ""
        echo "Available remotes:"
        local remotes; remotes=$(list_remotes)
        local i=1
        while IFS= read -r rname; do
            [[ -z "$rname" ]] && continue
            echo "  $i) $rname"
            ((i++)) || true
        done <<< "$remotes"
        echo ""
        read -rp "Target remotes (comma-separated names, empty=all): " sched_remotes
    fi

    # Write config
    mkdir -p "$SCHEDULES_DIR"
    cat > "$config_file" <<CONF
# gniza4cp schedule config: $name
# Generated by 'gniza4cp schedule add $name'
# $(date -u +"%d/%m/%Y %H:%M:%S UTC")

SCHEDULE="$sched_type"
SCHEDULE_TIME="$sched_time"
SCHEDULE_DAY="$sched_day"
SCHEDULE_CRON="$sched_cron"

REMOTES="$sched_remotes"
CONF

    echo ""
    echo "${C_GREEN}Schedule '$name' created: $config_file${C_RESET}"
    echo "Run 'gniza4cp schedule install' to activate cron entries."
}

_schedule_delete() {
    local name="$1"
    local config_file="$SCHEDULES_DIR/${name}.conf"

    if [[ ! -f "$config_file" ]]; then
        die "Schedule not found: $name (expected $config_file)"
    fi

    load_schedule "$name"
    echo "Schedule: $name"
    echo "Type:     ${SCHEDULE:-none}"
    echo "Time:     ${SCHEDULE_TIME:-02:00}"
    echo "Remotes:  ${SCHEDULE_REMOTES:-(all)}"
    echo ""
    read -rp "Delete schedule '$name'? [y/N] " answer
    [[ "$answer" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }

    rm -f "$config_file"
    echo "Schedule '$name' deleted."
    echo "Run 'gniza4cp schedule install' to update cron entries."
}

_schedule_run() {
    local name="$1"
    local config_file="$SCHEDULES_DIR/${name}.conf"

    if [[ ! -f "$config_file" ]]; then
        die "Schedule not found: $name (expected $config_file)"
    fi

    load_schedule "$name"

    local args=()
    if [[ -n "${SCHEDULE_REMOTES:-}" ]]; then
        args+=(--remote="$SCHEDULE_REMOTES")
    fi
    if [[ "${SCHEDULE_SYSBACKUP:-}" == "yes" ]]; then
        args+=(--sysbackup)
    fi
    if [[ "${SCHEDULE_SKIP_SUSPENDED:-}" == "yes" ]]; then
        args+=(--skip-suspended)
    fi

    echo "Running schedule '$name'..."
    echo "  Remotes:    ${SCHEDULE_REMOTES:-(all)}"
    echo "  Sysbackup:  ${SCHEDULE_SYSBACKUP:-no}"
    echo "  Skip suspended: ${SCHEDULE_SKIP_SUSPENDED:-no}"
    echo ""

    # Exec replaces this process with the backup command
    exec /usr/local/bin/gniza4cp backup "${args[@]}"
}

_schedule_list() {
    if ! has_schedules; then
        echo "No schedules configured."
        echo "Run 'gniza4cp schedule add <name>' to create one."
        return 0
    fi

    local schedules; schedules=$(list_schedules)
    local count; count=$(echo "$schedules" | wc -l)

    echo "${C_BOLD}Configured schedules ($count):${C_RESET}"
    echo ""
    printf "  %-15s %-10s %-8s %-6s %s\n" "NAME" "TYPE" "TIME" "DAY" "REMOTES"
    printf "  %-15s %-10s %-8s %-6s %s\n" "----" "----" "----" "---" "-------"

    while IFS= read -r sname; do
        [[ -z "$sname" ]] && continue
        load_schedule "$sname" 2>/dev/null || { printf "  %-15s %s\n" "$sname" "${C_RED}(failed to load)${C_RESET}"; continue; }

        printf "  %-15s %-10s %-8s %-6s %s\n" \
            "$sname" "${SCHEDULE:-none}" "${SCHEDULE_TIME:-02:00}" "${SCHEDULE_DAY:--}" "${SCHEDULE_REMOTES:-(all)}"
    done <<< "$schedules"
    echo ""
}

# ── System Backup / Restore ───────────────────────────────────

# Transfer + finalize + retention for system backup on the current remote.
# Globals REMOTE_* must already be set via load_remote().
_sysbackup_to_current_remote() {
    local stage_dir="$1"
    local ts="$2"
    local remote_label="$CURRENT_REMOTE_NAME"

    clean_partial_system_snapshots

    local prev; prev=$(get_latest_system_snapshot) || prev=""

    if ! transfer_system_backup "$stage_dir" "$ts" "$prev"; then
        log_error "[$remote_label] System backup transfer failed"
        return 1
    fi

    if ! finalize_system_snapshot "$ts"; then
        log_error "[$remote_label] System snapshot finalize failed"
        return 1
    fi

    enforce_system_retention
    return 0
}

cmd_sysbackup() {
    require_root
    local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
    load_config "$config_file"
    validate_config || die "Invalid configuration"
    init_logging
    log_info "[TYPE:SYSBACKUP] System backup started"

    local dry_run=false
    has_flag dry-run "$@" && dry_run=true

    local remote_flag=""
    remote_flag=$(get_opt remote "$@" 2>/dev/null) || true

    acquire_lock
    trap 'cleanup_system_stage; release_lock' EXIT

    local remotes=""
    remotes=$(get_target_remotes "$remote_flag") || die "Invalid remote specification"

    _save_remote_globals

    # Test connectivity upfront
    while IFS= read -r rname; do
        [[ -z "$rname" ]] && continue
        load_remote "$rname" || die "Failed to load remote: $rname"
        if _is_rclone_mode; then
            test_rclone_connection || die "Cannot connect to remote '$rname' (${REMOTE_TYPE})"
        else
            test_ssh_connection || die "Cannot connect to remote '$rname' (${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT})"
        fi
    done <<< "$remotes"
    _restore_remote_globals

    local start_time; start_time=$(date +%s)
    local ts; ts=$(timestamp)
    local temp_dir="${TEMP_DIR:-$DEFAULT_TEMP_DIR}"
    local stage_dir="$temp_dir/system/$ts"

    if [[ "$dry_run" == "true" ]]; then
        log_info "[DRY RUN] System backup preview"
        log_info "[DRY RUN] Would export: packages, tweaksettings, DNS zones, IPs, PHP config"
        log_info "[DRY RUN] Would stage: ${#_SYSBACKUP_PATHS[@]} system paths + ea-php configs"
        while IFS= read -r rname; do
            [[ -z "$rname" ]] && continue
            load_remote "$rname"
            log_info "[DRY RUN] [$rname] Would transfer system backup to ${REMOTE_HOST:-$REMOTE_TYPE}"
            log_info "[DRY RUN] [$rname] Would finalize system snapshot: $ts"
            log_info "[DRY RUN] [$rname] Would enforce retention: keep $RETENTION_COUNT"
        done <<< "$remotes"
        _restore_remote_globals
        echo ""
        echo "System backup dry run complete. No changes made."
        exit "$EXIT_OK"
    fi

    # Stage ONCE
    log_info "=== System Backup ==="
    if ! run_system_backup "$stage_dir"; then
        log_error "System backup staging failed"
        cleanup_system_stage
        exit "$EXIT_FATAL"
    fi

    # Transfer to each remote
    local failed=0
    local succeeded=0
    while IFS= read -r rname; do
        [[ -z "$rname" ]] && continue
        load_remote "$rname"
        log_info "--- Transferring system backup to remote '$rname' ---"
        if _sysbackup_to_current_remote "$stage_dir" "$ts"; then
            ((succeeded++)) || true
            log_info "System backup to '$rname' completed"
        else
            ((failed++)) || true
            log_error "System backup to '$rname' failed"
        fi
    done <<< "$remotes"
    _restore_remote_globals

    cleanup_system_stage

    local end_time; end_time=$(date +%s)
    local duration=$(( end_time - start_time ))

    echo ""
    echo "============================================"
    echo "System Backup Summary"
    echo "============================================"
    echo "Timestamp:   $ts"
    echo "Duration:    $(human_duration $duration)"
    echo "Remotes:     $(echo "$remotes" | tr '\n' ' ')"
    echo "Succeeded:   ${C_GREEN}${succeeded}${C_RESET}"
    if (( failed > 0 )); then
        echo "Failed:      ${C_RED}${failed}${C_RESET}"
    else
        echo "Failed:      0"
    fi
    echo "============================================"

    if (( failed > 0 && succeeded > 0 )); then
        exit "$EXIT_PARTIAL"
    elif (( failed > 0 )); then
        exit "$EXIT_FATAL"
    fi
    exit "$EXIT_OK"
}

cmd_sysrestore() {
    require_root
    local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
    load_config "$config_file"
    validate_config || die "Invalid configuration"
    init_logging

    local remote_flag=""
    remote_flag=$(get_opt remote "$@" 2>/dev/null) || true
    [[ -z "$remote_flag" ]] && die "Specify --remote=NAME for sysrestore."$'\n'"Available remotes: $(list_remotes | tr '\n' ' ')"

    _save_remote_globals
    load_remote "$remote_flag" || die "Failed to load remote: $remote_flag"

    local timestamp=""
    timestamp=$(get_opt timestamp "$@" 2>/dev/null) || true

    local dry_run=false
    has_flag dry-run "$@" && dry_run=true

    local phases="1,2,3,4"
    phases=$(get_opt phase "$@" 2>/dev/null) || phases="1,2,3,4"

    # Test connectivity
    if _is_rclone_mode; then
        test_rclone_connection || die "Cannot connect to remote '$remote_flag' (${REMOTE_TYPE})"
    else
        test_ssh_connection || die "Cannot connect to remote '$remote_flag' (${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT})"
    fi

    # Resolve timestamp
    local ts; ts=$(resolve_system_snapshot_timestamp "$timestamp") || die "No system snapshot found on remote '$remote_flag'"
    [[ -z "$ts" ]] && die "No system snapshot found on remote '$remote_flag'"

    log_info "System restore from remote '$remote_flag', snapshot: $ts"
    log_info "Phases: $phases, Dry-run: $dry_run"

    local temp_dir="${TEMP_DIR:-$DEFAULT_TEMP_DIR}"
    local stage_dir="$temp_dir/system-restore/$ts"

    # Download snapshot (needed for both dry-run and real restore)
    if ! download_system_snapshot "$ts" "$stage_dir"; then
        die "Failed to download system snapshot: $ts"
    fi

    # Run restore
    local rc=0
    run_system_restore "$stage_dir" "$phases" "$dry_run" || rc=$?

    # Cleanup staging
    if [[ -d "$stage_dir" ]]; then
        rm -rf "$stage_dir"
        log_debug "Cleaned up restore staging: $stage_dir"
    fi

    _restore_remote_globals

    if (( rc != 0 )); then
        echo ""
        echo "${C_YELLOW}System restore completed with errors. Check log for details.${C_RESET}"
        exit "$EXIT_PARTIAL"
    fi

    echo ""
    echo "${C_GREEN}System restore completed successfully.${C_RESET}"
    exit "$EXIT_OK"
}

cmd_stats() {
    local config_file; config_file=$(get_opt config "$@" 2>/dev/null) || config_file="$DEFAULT_CONFIG_FILE"
    load_config "$config_file"
    validate_config || die "Invalid configuration"
    init_logging

    local log_dir="${LOG_DIR:-$DEFAULT_LOG_DIR}"
    local stats_file="$log_dir/stats.json"
    local remotes; remotes=$(list_remotes) || true

    if [[ -z "$remotes" ]]; then
        log_error "No remotes configured"
        return 1
    fi

    local total_accounts=0
    local total_snapshots=0
    local remote_json_parts=()
    local -A seen_accounts

    _save_remote_globals
    while IFS= read -r rname; do
        [[ -z "$rname" ]] && continue
        load_remote "$rname" || { log_warn "Failed to load remote: $rname"; continue; }

        # Test connectivity
        local conn_ok=false
        if _is_rclone_mode; then
            test_rclone_connection 2>/dev/null && conn_ok=true
        else
            test_ssh_connection 2>/dev/null && conn_ok=true
        fi

        local r_accounts=0
        local r_snapshots=0

        if $conn_ok; then
            local accounts_list; accounts_list=$(list_remote_accounts 2>/dev/null) || true
            if [[ -n "$accounts_list" ]]; then
                while IFS= read -r acc; do
                    [[ -z "$acc" ]] && continue
                    ((r_accounts++)) || true
                    seen_accounts["$acc"]=1
                    local snaps; snaps=$(list_remote_snapshots "$acc" 2>/dev/null) || true
                    if [[ -n "$snaps" ]]; then
                        local snap_count; snap_count=$(echo "$snaps" | wc -l)
                        ((r_snapshots += snap_count)) || true
                    fi
                done <<< "$accounts_list"
            fi
        else
            log_warn "Cannot connect to remote: $rname (skipping)"
        fi

        ((total_snapshots += r_snapshots)) || true
        remote_json_parts+=("\"$rname\":{\"accounts\":$r_accounts,\"snapshots\":$r_snapshots}")
    done <<< "$remotes"
    _restore_remote_globals

    total_accounts=${#seen_accounts[@]}

    # Parse last backup status from most recent log file
    local last_status="UNKNOWN"
    local last_log=""
    local latest_log=""
    if [[ -d "$log_dir" ]]; then
        latest_log=$(ls -1t "$log_dir"/gniza4cp-[0-9]*-[0-9]*.log 2>/dev/null | head -1) || true
    fi
    if [[ -n "$latest_log" && -f "$latest_log" ]]; then
        last_log=$(basename "$latest_log")
        if tail -20 "$latest_log" | grep -qi "Backup completed successfully"; then
            last_status="SUCCESS"
        elif tail -20 "$latest_log" | grep -qi "Backup failed\|partial failure\|PARTIAL"; then
            last_status="FAILURE"
        fi
    fi

    # Build JSON
    local updated; updated=$(date -u +"%d/%m/%Y %H:%M:%S")
    local remotes_json; remotes_json=$(IFS=','; echo "${remote_json_parts[*]}")
    local json="{\"updated\":\"$updated\",\"backed_up_accounts\":$total_accounts,\"snapshots\":$total_snapshots,\"remotes\":{$remotes_json},\"last_backup\":{\"status\":\"$last_status\",\"log\":\"$last_log\"}}"

    echo "$json" > "$stats_file"
    log_info "Stats written to $stats_file"
    echo "$json"
}

cmd_usage() {
    cat <<EOF
${C_BOLD}gniza4cp v${GNIZA4CP_VERSION}${C_RESET} — cPanel Backup, Restore & Disaster Recovery

${C_BOLD}Usage:${C_RESET}
  gniza4cp <command> [options]

${C_BOLD}Commands:${C_RESET}
  backup    [--account=NAME] [--remote=NAME[,NAME2]] [--dry-run] [--sysbackup] [--skip-suspended]
  restore   account <name> [--remote=NAME] [--timestamp=TS] [--force]
  restore   files <name> [--remote=NAME] [--path=subpath] [--timestamp=TS]
  restore   database <name> [<dbname>] [--remote=NAME] [--timestamp=TS]
  restore   mailbox <name> [<email@domain>] [--remote=NAME] [--timestamp=TS]
  restore   cron <name> [--remote=NAME] [--timestamp=TS]
  restore   dbusers <name> [--remote=NAME] [--timestamp=TS]
  restore   cpconfig <name> [--remote=NAME] [--timestamp=TS]
  restore   domains <name> [--remote=NAME] [--timestamp=TS]
  restore   ssl <name> [--remote=NAME] [--timestamp=TS]
  restore   list-databases <name> [--remote=NAME] [--timestamp=TS]
  restore   list-mailboxes <name> [--remote=NAME] [--timestamp=TS]
  restore   list-files <name> [--remote=NAME] [--timestamp=TS] [--path=subdir]
  restore   server [--remote=NAME] [--timestamp=TS]
  sysbackup [--remote=NAME[,NAME2]] [--dry-run]   Backup system/WHM config
  sysrestore --remote=NAME [--timestamp=TS] [--phase=N] [--dry-run]
  list      [--account=NAME] [--remote=NAME]   List remote snapshots
  verify    [--account=NAME] [--remote=NAME]   Verify backup integrity
  status                                        Show configuration and status
  remote    list                                List configured remotes
  remote    delete <name>                       Remove a remote destination
  schedule  add <name>                          Create a backup schedule
  schedule  delete <name>                       Remove a schedule
  schedule  run <name>                          Run a schedule now
  schedule  list                                Show configured schedules
  schedule  {install|show|remove}               Manage cron entries
  stats                                         Collect backup statistics
  version                                       Show version

${C_BOLD}Global Options:${C_RESET}
  --config=PATH    Use alternate config file (default: /etc/gniza4cp/gniza4cp.conf)
  --remote=NAME    Target specific remote(s), comma-separated
  --debug          Enable debug logging

${C_BOLD}Examples:${C_RESET}
  gniza4cp backup --dry-run
  gniza4cp backup --account=johndoe
  gniza4cp backup --remote=nas
  gniza4cp backup --remote=nas,offsite
  gniza4cp list --remote=offsite
  gniza4cp restore files johndoe --remote=nas --path=public_html
  gniza4cp restore database johndoe johndoe_wp --remote=nas
  gniza4cp restore mailbox johndoe info@example.com --remote=nas
  gniza4cp schedule add nightly
  gniza4cp schedule list
  gniza4cp schedule install
  gniza4cp remote list
  gniza4cp sysbackup --dry-run
  gniza4cp sysbackup --remote=nas
  gniza4cp sysrestore --remote=nas
  gniza4cp sysrestore --remote=nas --phase=1 --dry-run
EOF
}

# ── Main routing ───────────────────────────────────────────────

main() {
    # Global --debug flag (used by config.sh load_config)
    # shellcheck disable=SC2034
    has_flag debug "$@" && GNIZA4CP_DEBUG=true || GNIZA4CP_DEBUG=false

    local command="${1:-}"
    shift 2>/dev/null || true

    case "$command" in
        backup)     cmd_backup "$@" ;;
        sysbackup)  cmd_sysbackup "$@" ;;
        sysrestore) cmd_sysrestore "$@" ;;
        restore)    cmd_restore "$@" ;;
        list)     cmd_list "$@" ;;
        verify)   cmd_verify "$@" ;;
        status)   cmd_status "$@" ;;
        remote)   cmd_remote "$@" ;;
        schedule) cmd_schedule "$@" ;;
        stats)    cmd_stats "$@" ;;
        version)  echo "gniza4cp v${GNIZA4CP_VERSION}" ;;
        help|-h|--help) cmd_usage ;;
        "")       cmd_usage ;;
        *)        die "Unknown command: $command"$'\n'"Run 'gniza4cp help' for usage" ;;
    esac
}

main "$@"
