#!/bin/bash

set -e
. /usr/share/bash-argsparse/argsparse.sh
. display.sh
. tmpfiles.sh
. lockfile.sh
set +e

CONFIGDIR="/etc/syshawk/backup"
RESTIC_LOGLEVEL=( --quiet )
RESTIC_PARAMS=( --cache-dir /var/cache/restic )
RESTIC_EXCLUDE=()
RESTIC_CLEAN=( --keep-daily 15 --keep-monthly 7 --keep-yearly 2 )
MONITOR_FILE=""
SSH_PARAMS=()
SSH_CONFIG="$CONFIGDIR/ssh_config"
DOTRACE1=0
DOTRACE2=0
DOTRACE3=0
DOTRACE4=0
declare -a stopWord=( "backup" "list" "lists" "snapshot" "snapshots" "restore" "raw" )

##############################################################################
# Generate Backup Script
generateBackupScript() {
    serveurconfig="$1"; shift
    serveur="$1"; shift
    script="$1"; shift

    display_informational "Reading config for %s" "$serveur"

    # Header
    (
        cat <<EOF
#!/bin/bash

[[ $DOTRACE4 -eq 1 ]] && set -x

doexit() {
    rm -f "\$0"
}

trap doexit EXIT

declare -i retour=0
declare -a listbackupmountpoint=()
printf -v start_ts "%(%s)T" -1

EOF

    for i in "${RESTIC_ENV[@]}"; do
        declare -p $i
    done

    printf "\n"
    ) > "$script"

    # Body
    while read mountpoint; do
        if [[ "$mountpoint" =~ ^PREHOOK:[[:space:]]*(.*)$ ]]; then
            PREHOOK_CMD="${BASH_REMATCH[1]}"
            display_notice "Prehook on %s: %s" "$serveur" "$PREHOOK_CMD"
            (
                printf "# Prehook\n"
                declare -p PREHOOK_CMD
                cat <<EOF
[[ $DOTRACE2 -eq 1 ]] && printf "%s: Lancement de %s\n" "\$HOSTNAME" "\$PREHOOK_CMD"
$PREHOOK_CMD
retour=\$(( \$? + \$retour ))
unset -v PREHOOK_CMD

EOF
            ) >> "$script"
            continue
        fi

        display_notice "Backing-up on %s:%s" "$serveur" "$mountpoint"
        (
            cat <<EOF
if [[ -e "$mountpoint" ]]; then
    [[ $DOTRACE2 -eq 1 ]] && printf "%s: Ajout de %s\n" "\$HOSTNAME" "$mountpoint"
    listbackupmountpoint=( "\${listbackupmountpoint[@]}" "$mountpoint" )
fi

EOF
        ) >> "$script"
    done < <( cat $serveurconfig | grep -Ev '^[[:space:]]*(#|$)' | sed -e 's/^[[:space:]]*//; s/[[:space:]]*$//;' )

    # Footer
    CMD=( sudo restic backup "${RESTIC_LOGLEVEL[@]}" "${RESTIC_PARAMS[@]}" "--force" "${RESTIC_EXCLUDE_PARAMS[@]}" )
    display_debug "Ligne de commande local: %s" "${CMD[*]}"
    declare -p CMD >> "$script"
    cat <<EOF >> "$script"
if [[ \${#listbackupmountpoint[@]} -gt 0 ]]; then
    [[ $DOTRACE2 -eq 1 ]] && printf "%s: Lancement de %s %s\n" "\$HOSTNAME" "\${CMD[*]}" "\${listbackupmountpoint[*]}"
    "\${CMD[@]}" "\${listbackupmountpoint[@]}"
    retour=\$(( \$? + \$retour ))
fi
unset -v CMD

printf -v end_ts "%(%s)T" -1

if [[ -n "$MONITOR_FILE" ]]; then
    (
        printf "%d\n" \$start_ts
        printf "%d\n" \$end_ts
        printf "%d\n" \$retour
        printf "%s\n" "\${listbackupmountpoint[@]}"
    ) > "$MONITOR_FILE"
fi

if [[ \$retour -eq 0 ]]; then
    exit 0
fi
exit 1
EOF

    if [[ $DOTRACE3 -eq 1 ]]; then
        printf "Script generee pour %s:\n" "$serveur"
        cat "$script"
    fi
    chmod +x "$script"
}

##############################################################################
# Do Backup
doBackup() {
    shopt -qs nullglob
    shopt -qu failglob
    for serveurconfig in $CONFIGDIR/hosts/*; do
        serveur=$( basename $serveurconfig )
        script=$( mktemp "backup.$$.XXXXXX" )

        generateBackupScript "$serveurconfig" "$serveur" "$script"

        [[ $DOTRACE1 -eq 1 ]] && printf "%s: Lancement de scp -q %s %s %s:\n" "$HOSTNAME" "${SSH_PARAMS[*]}" "$script" "$serveur"
        scp -q "${SSH_PARAMS[@]}" "$script" "$serveur":
        if [[ $? -ne 0 ]]; then
            display_critical "Failed backup: %s" "$serveur"
            continue
        fi

        [[ $DOTRACE1 -eq 1 ]] && printf  "%s: Lancement de ssh %s %s -- ./%s\n" "$HOSTNAME" "${SSH_PARAMS[*]}" "$serveur" $( basename "$script" )
        ssh "${SSH_PARAMS[@]}" "$serveur" -- ./$( basename "$script" )
        if [[ $? -ne 0 ]]; then
            display_critical "Failed backup: %s" "$serveur"
            continue
        fi
    done

    restic forget "${RESTIC_LOGLEVEL[@]}" "${RESTIC_PARAMS[@]}" --prune "${RESTIC_CLEAN[@]}"
    if [[ $? -ne 0 ]]; then
        display_critical "Cleaning old backup failed"
        return 1
    fi
}

##############################################################################
# Do Snapshot
doSnapshots() {
    display_notice "Listing des backup"
    display_debug "Ligne de commande local: restic snapshots %s %s" "${RESTIC_PARAMS[*]}" "$*"
    restic snapshots "${RESTIC_PARAMS[@]}" "$@"
}

##############################################################################
# Do Restore
doRestore() {
    display_notice "Restauration de backup"
    display_debug "Ligne de commande local: restic restore %s %s" "${RESTIC_PARAMS[*]}" "$*"
    restic restore "${RESTIC_PARAMS[@]}" "$@"
}

##############################################################################
# Do Raw
doRaw() {
    display_notice "Raw command"
    display_debug "Ligne de commande local: restic %s %s" "${RESTIC_PARAMS[*]}" "$*"
    restic "${RESTIC_PARAMS[@]}" "$@"
}

##############################################################################
# Main
alreadyStopped=0
declare -a newCmdLine=()
for i in "$@"; do
    if [[ "$i" == '--' ]]; then
        alreadyStopped=1
    fi
    if [[ $alreadyStopped -eq 0 ]]; then
        for j in "${stopWord[@]}"; do
            if [[ "$i" == "$j" ]]; then
                newCmdLine+=( '--' )
                alreadyStopped=1
            fi
        done
    fi
    newCmdLine+=( "$i" )
done
unset -v alreadyStopped

printf -v argsparse_usage_description "%s\n" \
    "Command:" \
    " $0 backup    : Lance un backup" \
    " $0 snapshots : Liste les backup (options: voir man restic)" \
    " $0 restore   : Lance une restauration (options: voir man restic)" \
    " $0 raw       : Lance une command via restic (options: voir man restic)"
argsparse_use_option verbose "Make more verbose (max: 9)" short:v
argsparse_use_option config-dir "Configuration directory" value "default:$CONFIGDIR"
argsparse_parse_options "${newCmdLine[@]}"
unset -v newCmdLine

if argsparse_is_option_set "verbose"; then
    loglevel=$(( 5 - ${program_options[verbose]} ))
    case $loglevel in
        5|4|3|2|1)
            display_set_mode $loglevel
            ;;
        *)
            display_set_mode 0
            RESTIC_LOGLEVEL=()
            if [[ $loglevel -le -1 ]]; then
                DOTRACE1=1
            fi
            if [[ $loglevel -le -2 ]]; then
                DOTRACE2=1
            fi
            if [[ $loglevel -le -3 ]]; then
                DOTRACE3=1
            fi
            if [[ $loglevel -le -4 ]]; then
                DOTRACE4=1
                set -x
            fi
    esac
fi

if argsparse_is_option_set "config-dir"; then
    CONFIGDIR="${program_options[config-dir]}"
fi

display_debug "Loading config"

set -e
. "$CONFIGDIR/config"
set +e

if [[ -n "$SSH_CONFIG" ]]; then
    SSH_PARAMS+=( -F "$SSH_CONFIG" )
fi

RESTIC_EXCLUDE_PARAMS=()
for i in "${RESTIC_EXCLUDE[@]}"; do
    RESTIC_EXCLUDE_PARAMS+=( --exclude "$i" )
done

for i in "${RESTIC_ENV[@]}"; do
    export $i
done

case "${program_params[0]}" in
    backup)
        if [[ ${#program_params[@]} -gt 1 ]]; then
            usage
            exit 1
        fi
        doBackup
        ;;
    list|lists|snapshot|snapshots)
        doSnapshots "${program_params[@]:1}"
        ;;
    restore)
        doRestore "${program_params[@]:1}"
        ;;
    raw)
        doRaw "${program_params[@]:1}"
        ;;
    *)
        usage
        exit 1
esac