#!/usr/bin/env bash
SCRIPT_PATH=$(realpath -s "$0")
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
SCRIPT=$(basename "$SCRIPT_PATH")
ETC_DIR="/etc/$SCRIPT"

function usage() {
  cat <<EOF
Usage: $SCRIPT COMMAND

Helper script for proxmox-backup-client.

Available commands:

    backup                               Run backup and print output to screen.
    backup-cron                          Run backup and suppress output on success.
    list                                 List groups, latest snapshots and archives.
    snapshot-list [GROUP]                List snapshots, sizes and archives.
    catalog-shell [SNAPSHOT [ARCHIVE]]   Run interactive shell to explore arhive contents
                                         and restore files/dirs selectively.
    mount [SNAPSHOT [ARCHIVE [TARGET]]]  Mount snapshot archive locally.

    install [--update]                   Install proxmox-backup-client binary and dependencies.
                                         Use --update to update the currently installed binary.
EOF
}

function cleanup_tmpdir() {
  rm -r "$tmpdir"
  trap - EXIT
}

function pbc-install() {
  local update=${1:-false}

  if $update || [ ! -f "$PBC" ]; then
    local pkgfile=$(wget -q -O - "$PBC_DOWNLOAD_URL/" | grep proxmox-backup-client-static_ | tail -n 1 | sed 's#.*href="##g;s#".*##g;')
    [ -z "$pkgfile" ] && echo "Unable to determine current package file!" >&2 && exit 1

    tmpdir=$(mktemp -d)
    trap cleanup_tmpdir EXIT

    echo "Downloading $PBC_DOWNLOAD_URL/$pkgfile ..."
    ! wget -q -O "$tmpdir/$pkgfile" "$PBC_DOWNLOAD_URL/$pkgfile" && echo "Error downloading package file!" >&2 && exit 2

    echo "Extracting $pkgfile ..."
    ! dpkg-deb -x "$tmpdir/$pkgfile" "$tmpdir/" && echo "Error extracting package file!" >&2 && exit 3

    echo "Installing proxmox-backup-client binary to $PBC ..."
    ! cp "$tmpdir/usr/bin/proxmox-backup-client" "$PBC" && echo "Error copying proxmox-backup-client binary!" >&2 && exit 4

    local version=$("$PBC" version | head -n 1)
    (( $? != 0 )) && echo "Error running $PBC version ... exited with rc=$?" >&2 && exit 5
    echo "Successfully installed proxmox-backup-$version"
  else
    local version=$("$PBC" version | head -n 1)
    (( $? != 0 )) && echo "Error running $PBC version ... exited with rc=$?" >&2 && exit 5
    echo "Binary proxmox-backup-client ($version) already installed"
  fi

  local jq=$(which jq)
  if [ -z "$jq" ]; then
    echo "Installing package jq ..."
    ! apt-get install jq && echo "Error installing jq package!" >&2 && exit 6
  else
    echo "Package jq already installed"
  fi
  if [ ! -d "$ETC_DIR" ]; then
    echo "Creating config directory $ETC_DIR ..."
    mkdir -p "$ETC_DIR"
  fi

  if [ -f "$SCRIPT_DIR/config.example" ]; then
    if $update || [ ! -f "$ETC_DIR/config.example" ]; then
      echo "Copy example config to $ETC_DIR/config.example ..."
      cp "$SCRIPT_DIR/config.example" "$ETC_DIR/config.example"
    fi
  fi
  echo "Installation successful!"
}

function pbc-backup() {
  "$PBC" backup --ns "$PBS_NAMESPACE" "${BACKUP[@]}"
  return $?
}

function pbc-list() {
  "$PBC" list --ns "$PBS_NAMESPACE" "$@"
  return $?
}

function pbc-snapshot-list() {
  "$PBC" snapshot list --ns "$PBS_NAMESPACE" "$@"
  return $?
}

function pbc-files() {
  "$PBC" files --ns "$PBS_NAMESPACE" "$@"
  return $?
}

function pbc-catalog-shell() {
  "$PBC" catalog shell --ns "$PBS_NAMESPACE" "$@"
  return $?
}

function pbc-mount() {
  "$PBC" mount --ns "$PBS_NAMESPACE" "$@"
  return $?
}

function get_group_names() {
  pbc-list --output-format json | jq -r '.[] | "\(.["backup-type"])/\(.["backup-id"])"'
}

function get_snapshot_names() {
  pbc-snapshot-list --output-format json | jq -r '.[] | "\(.["backup-type"])/\(.["backup-id"])/\(.["backup-time"] | todateiso8601)"'
}

function get_snapshot_files() {
  pbc-files "$1" --output-format json | jq -r '.[] | select(.filename | endswith(".pxar.didx")) | .filename | rtrimstr(".didx")' | sort
}

function arr_contains() {
  search=$1
  shift
  [ -z "$search" ] && return 1
  while [ -n "$1" ]; do
    [ "$1" == "$search" ] && return 0
    shift
  done
  return 1
}

function may_select() {
  value=$1
  shift

  (( $# == 0 )) && echo "Error: no values to select" >&2 && return 1

  if [ -z "$value" ]; then
    if (( $# == 1 )); then
      value=$1
    else
      sorted=($(printf "%s\n" "$@" | sort))
      select value in "${sorted[@]}"; do
        [ -n "$value" ] && break
      done
    fi
  elif ! arr_contains "$value" "$@"; then
    return 1
  fi

  echo "$value"
  return 0
}

action=''
configfile=''
args=()

while [ -n "$1" ]; do
  opt=$1
  shift
  if [ -z "$action" ]; then
    case "$opt" in
      backup|backup-cron|list|snapshot-list|catalog-shell|mount|install)
        action=$opt
      ;;
      -c|--config)
        configfile=$1
        shift
        [ -z "$configfile" ] && usage && exit 255
      ;;
      -h|--help)
        usage
        exit 0
      ;;
      *)
        usage
        exit 255
      ;;
    esac
  elif [ "$action" == "install" ]; then
    case "$opt" in
      -u|--update)
        args+=('true')
      ;;
      *)
        usage
        exit 255
      ;;
    esac
  else
    args+=("$opt")
  fi
done

configfile=${configfile:-$ETC_DIR/config}
if [ "$action" == 'install' ]; then
  if [ -f "$configfile" ]; then
    source "$configfile" || exit $?
  fi
else
  source "$configfile" || exit $?
  [ ! -f "$PBC" ] && echo "Error: $PBC: no such file" >&2 && exit 1
  [ ! -e "$PBC" ] && echo "Error: $PBC: not executable" >&2 && exit 1
fi

PBC=${PBC:-/usr/bin/proxmox-backup-client}
PBC_DOWNLOAD_URL=${PBC_DOWNLOAD_URL:-http://download.proxmox.com/debian/pbs-client/dists/trixie/main/binary-amd64}

export PBS_REPOSITORY="$PBS_USER@${PBS_SERVER:-8007}:$PBS_DATASTORE"
export PBS_PASSWORD="$PBS_PASSWORD"

case "$action" in
  backup)
    pbc-backup || exit $?
  ;;
  backup-cron)
    output=$(pbc-backup 2>&1)
    rc=$?
    (( $rc != 0 )) && echo "$output"
  ;;
  list)
    pbc-list "${args[@]}" || exit $?
  ;;
  snapshot-list)
    readarray -t groups < <(get_group_names) 
    (( ${#groups[@]} == 0 )) && echo "no snapshot groups found" && exit 0

    group=${args[0]}
    args=("${args[@]:1}")

    PS3="Snapshot group [1-${#groups[@]}]: "
    group=$(may_select "$group" "${groups[@]}")
    (( $? != 0 )) && echo "Error: invalid group" >&2 && exit 1

    echo
    pbc-snapshot-list "$group" "${args[@]}" || exit $?
  ;;
  catalog-shell)
    readarray -t snapshots < <(get_snapshot_names)
    (( ${#snapshots[@]} == 0 )) && echo "no snapshots found" && exit 0

    snapshot=${args[0]}
    args=("${args[@]:1}")

    PS3="Snapshot [1-${#snapshots[@]}]: "
    snapshot=$(may_select "$snapshot" "${snapshots[@]}")
    (( $? != 0 )) && echo "Error: invalid snapshot" >&2 && exit 1

    readarray -t archives < <(get_snapshot_files "$snapshot")
    (( ${#archives[@]} == 0 )) && echo "no archives found" && exit 0

    archive=${args[0]}
    args=("${args[@]:1}")

    PS3="Archive [1-${#archives[@]}]: "
    archive=$(may_select "$archive" "${archives[@]}")
    (( $? != 0 )) && echo "Error: invalid archive" >&2 && exit 1

    echo
    pbc-catalog-shell "$snapshot" "$archive" || exit $?
  ;;
  mount)
    readarray -t snapshots < <(get_snapshot_names)
    (( ${#snapshots[@]} == 0 )) && echo "no snapshots found" && exit 0

    snapshot=${args[0]}
    args=("${args[@]:1}")

    PS3="Snapshot [1-${#snapshots[@]}]: "
    snapshot=$(may_select "$snapshot" "${snapshots[@]}")
    (( $? != 0 )) && echo "Error: invalid snapshot" >&2 && exit 1

    readarray -t archives < <(get_snapshot_files "$snapshot")
    (( ${#archives[@]} == 0 )) && echo "no archives found" && exit 0

    archive=${args[0]}
    args=("${args[@]:1}")

    PS3="Archive [1-${#archives[@]}]: "
    archive=$(may_select "$archive" "${archives[@]}")
    (( $? != 0 )) && echo "Error: invalid archive" >&2 && exit 1

    target=${args[0]}
    args=("${args[@]:1}")

    if [ -z "$target" ]; then
      while [ -z "$target" ]; do
        echo -n "Target path: "
        read -r target
        [ -d "$target" ] && break
        echo -e "\nError: $target: no such directory\n" >&2
	target=''
      done
    elif [ ! -d "$target" ]; then
      echo "Error: $target: no such directory" >&2
      exit 1
    fi

    echo
    pbc-mount "$snapshot" "$archive" "$target" || exit $?
  ;;
  install)
    pbc-install "${args[@]}" || exit $?
  ;;
  *)
    usage
    exit 1
  ;;
esac
