#! /bin/bash
# vim: shiftwidth=4 tabstop=4 expandtab

if in_array "${COMMAND_ARGS[0]}" --help -h help; then
    echo "$COMMAND [VM name] [hyp]"
    if [ "${COMMAND_ARGS[1]}" == "details" ]; then
        echo "  Restore VM specified by name from backup"
    fi
    return
fi

# shellcheck source=/dev/null
source "$LIB_DIR"/backup
_backup_dir BACKUP_DIR

VM=${COMMAND_ARGS[0]}
[[ -z "$VM" ]] && usage "VM name is missing"

ME=$(hostname -s)
HYP=${COMMAND_ARGS[1]:-$ME}
check_hyp "$HYP" || usage "Unknown hypervisor $HYP"

# Check if VM is running somewhere
CUR_HYP=$(CACHE_LIFETIME=0 locate "$VM")
if [[ -n "$CUR_HYP" ]]; then
    VM_STATE=$(vm_state "$VM" "$CUR_HYP")
    [[ "$VM_STATE" != "shut off" ]] && \
        fatal_error "VM $VM is $VM_STATE on $CUR_HYP." \
            "Please stop it before trying to restore it from backups."
    log INFO "VM $VM is defined (and stopped) on $CUR_HYP"
    if [[ "$CUR_HYP" == "$HYP" ]]; then
        display "VM $VM already configured on this $HYP:" \
            "we need to undefine it firstly on $HYP to continue."
        [[ $AUTO -eq 1 ]] && [[ $FORCE -ne 1 ]] && \
            fatal_error "Auto mode enabled without force mode, deny this action."
        confirm || exit 0
    elif confirm "Backup VM $VM configuration on $CUR_HYP"; then
        "$(_vm_command_path)" backup "$VM" || \
            fatal_error "Failed to backup VM $VM configuration on $CUR_HYP"
        display "VM $VM configuration backup on $CUR_HYP."
    fi
    # Undefine VM if we need to restore it on same hyp, otherwise, let user choose
    if [[ "$HYP" == "$CUR_HYP" ]] || confirm "Undefine VM $VM on $CUR_HYP"; then
        if run_on_hyp "$CUR_HYP" "$REAL_VIRSH" undefine "$VM"; then
            display "VM $VM undefined on $CUR_HYP"
        else
            fatal_error "Failed to undefine VM $VM on $CUR_HYP"
        fi
    fi
else
    log INFO "VM $VM not found"
fi

# Handle temporary files
LOCAL_TMP_FILE=""
REMOTE_TMP_FILE=""
function remove_tmp_files() {
    if [[ -n "$LOCAL_TMP_FILE" ]]; then
        log DEBUG "Remove temporary file ($LOCAL_TMP_FILE)"
        if rm -f "$LOCAL_TMP_FILE"; then
            log INFO "Local temporary file $LOCAL_TMP_FILE removed"
            LOCAL_TMP_FILE=""
        else
            log WARNING "Failed to remove local temporary file $LOCAL_TMP_FILE"
        fi
    fi
    if [[ -n "$REMOTE_TMP_FILE" ]]; then
        log DEBUG "Remove temporary file ($REMOTE_TMP_FILE) on $HYP"
        if run_on_hyp "$HYP" rm -f "$REMOTE_TMP_FILE"; then
            log INFO "Temporary file $REMOTE_TMP_FILE removed on $HYP"
            REMOTE_TMP_FILE=""
        else
            log WARNING "Failed to remove temporary file $REMOTE_TMP_FILE on $HYP"
        fi
    fi
}

# Locate VM's backup file
BACKUP_FILE="$BACKUP_DIR/$VM.xml"
BACKUP_HYP=""
BACKUP_DATE=""
if [[ -e "$BACKUP_FILE" ]]; then
    log INFO "Actual backup found for VM $VM"
else
    mapfile -t BACKUP_FILES < <(
        find "$BACKUP_DIR" -type f -name "$VM.xml" -printf "%T@ %p\n" | sort -nr
    )
    if [[ "${#BACKUP_FILES[@]}" -eq 0 ]]; then
        log INFO "No actual backup found for VM $VM, lookup in git history"
        # Looking for the second last commit about this file (the first is about its deletion)
        COMMIT_ID=$(
            git --git-dir="$BACKUP_DIR/.git" rev-list -n 2 HEAD -- "$VM.xml" | tail -n 1
        ) || \
            fatal_error "Failed to locate backup of VM $VM in git history"
        [[ -z "$COMMIT_ID" ]] && fatal_error "No backup found for VM $VM in git history"
        BACKUP_DATE=$(git --git-dir="$BACKUP_DIR/.git" show -s --format=%ci "$COMMIT_ID")
        log INFO "Backup of VM $VM found in git history (commit: $COMMIT_ID, date: $BACKUP_DATE)"
        LOCAL_TMP_FILE=$(mktemp -t vm-restore-"${VM}"-XXX --suffix .xml)
        if REAL_PATH=$(git --git-dir="$BACKUP_DIR/.git" show "$COMMIT_ID:$VM.xml"); then
            log DEBUG "Real path of VM $VM backup in git commit $COMMIT_ID is '$REAL_PATH'"
            BACKUP_HYP=$(dirname "$REAL_PATH")
            if git --git-dir="$BACKUP_DIR/.git" show "$COMMIT_ID:$REAL_PATH" > "$LOCAL_TMP_FILE"; then
                log INFO "VM $VM backup restored from git commit $COMMIT_ID in temporary file $LOCAL_TMP_FILE"
                BACKUP_FILE="$LOCAL_TMP_FILE"
            else
                remove_tmp_files
                fatal_error "Failed to restore VM $VM backup from git commit $COMMIT_ID"
            fi
        else
            remove_tmp_files
            fatal_error "Failed to identify real path of VM $VM backup in commit $COMMIT_ID"
        fi
    elif [[ "${#BACKUP_FILES[@]}" -eq 1 ]] || [[ "$AUTO" -eq 1 ]]; then
        BACKUP_FILE=$( cut -d' ' -f2- <<< "${BACKUP_FILES[0]}" )
        [[ "$AUTO" -eq 1 ]] && \
            log INFO "Auto-mode: selecting the most recent backup ($BACKUP_FILE)."
    else
        BACKUP_FILE=""
        while [[ -z "$BACKUP_FILE" ]]; do
            C=0
            echo "Multiple backups found for VM $VM:"
            for backup_file in "${BACKUP_FILES[@]}"; do
                (( C+=1 ))
                BKP_HYP=$(basename "$( dirname "$( cut -d' ' -f2- <<< "$backup_file" )" )")
                BKP_DATE=$(date -d "@$( cut -d' ' -f1 <<< "$backup_file" )" "+%Y-%m-%d %H:%M:%S")
                echo "$C - $BKP_HYP ($BKP_DATE)"
            done
            echo -n "Please select on backup: "
            read -r a
            if ! check_int "$a" || [[ "${BACKUP_FILES[$((a-1))]:-null}" == "null" ]]; then
                echo "Invalid choice"
            else
                BACKUP_FILE=$( cut -d ' ' -f2- <<< "${BACKUP_FILES[$((a-1))]}" )
            fi
        done
    fi
fi
[[ -z "$BACKUP_HYP" ]] && \
    BACKUP_HYP=$(basename "$( dirname "$(realpath "$BACKUP_FILE")" )")
[[ -z "$BACKUP_DATE" ]] && BACKUP_DATE=$(stat -c "%y" "$BACKUP_FILE")
confirm "Restore VM $VM from backup of $BACKUP_HYP on $BACKUP_DATE ($BACKUP_FILE)" || exit 0

if [[ "$HYP" != "$ME" ]]; then
    log INFO "Copy backup file to $HYP"
    REMOTE_TMP_FILE=$( run_on_hyp "$HYP" mktemp -t vm-restore-"${VM}"-XXX --suffix .xml ) || \
        fatal_error "Error to create temporary file on $HYP"
    if scp "$BACKUP_FILE" "$HYP:$REMOTE_TMP_FILE"; then
        log INFO "Backup file copied on $HYP ($BACKUP_FILE -> $REMOTE_TMP_FILE)"
        BACKUP_FILE=$REMOTE_TMP_FILE
    else
        remove_tmp_files
        fatal_error "Failed to copy backup file on $HYP"
    fi
fi

if run_on_hyp "$HYP" "$REAL_VIRSH" define "$BACKUP_FILE"; then
    display "VM $VM restore on $HYP"
else
    remove_tmp_files
    fatal_error "Failed to restore VM $VM on $HYP"
    exit 1
fi
remove_tmp_files

confirm "Start VM $VM on $HYP" || exit 0
run_on_hyp "$HYP" "$REAL_VIRSH" "$VM"
