#!/usr/bin/env bash

VERSION=0.2.1
URL="http://download.tumbleweed.boombatower.com"
CONFIG_DIR="/etc/zypp"
VARS_DIR="$CONFIG_DIR/vars.d"
VAR_NAME="snapshotVersion"
VAR_FILE="$VARS_DIR/$VAR_NAME"
SNAPSHOT_HISTORY="$VARS_DIR/.snapshotVersion.history"
REPOS_DIR="/etc/zypp/repos.d"
# REPO_PATTERN="http://download.opensuse.org/(?:[^u][^/]+/)?tumbleweed/[^$]+"
# Disable matching debug and source URLs for the time being.
REPO_PATTERN="http://download.opensuse.org/tumbleweed/[^$]+"

tumbleweed_sudo()
{
  # Obtain sudo before issuing real commands to avoid half completion.
  if ! sudo cat /dev/null ; then
    echo "unable to obtain sudo"
    exit 1
  fi
}

tumbleweed_inited()
{
  if [ -f "$VAR_FILE" ] ; then
    return 0
  fi
  return 1
}

tumbleweed_init()
{
  if tumbleweed_inited && [ "$force" != "1" ] ; then
    echo "already initialized"
    return
  fi

  tumbleweed_sudo
  tumbleweed_history_init
  tumbleweed_variable "$(tumbleweed_installed)"
  tumbleweed_repo_init
}

tumbleweed_variable()
{
  if [ $# -eq 1 ] ; then
    local version="$1"
    echo "$version" | sudo tee "$VAR_FILE" > /dev/null
    if [ $? -ne 0 ] ; then
      echo "failed to set version to $version in $VARS_DIR/$VAR_NAME"
      exit 1
    fi
    tumbleweed_history "$version"
  else
    cat "$VAR_FILE"
  fi
}

tumbleweed_repo_init()
{
  if [ ! -d "$REPOS_DIR/.previous" ] ; then
    sudo mkdir "$REPOS_DIR/.previous"
  fi

  local IFS=$'\n' # Handle repo files with space in name. :(
  local files=($(grep -lP "$REPO_PATTERN" "$REPOS_DIR"/*))
  local file
  for file in ${files[@]} ; do
    echo "backup $file"
    sudo cp --backup=numbered "$file" "$REPOS_DIR/.previous/$(basename "$file")"
    sudo sed -i -r 's|(name=.*)|\1 ($snapshotVersion)|' "$file"
    sudo sed -i -r 's|(baseurl=).*tumbleweed/(.*)|\1'$URL'/$snapshotVersion/\2|' "$file"
  done
}

tumbleweed_history_init()
{
  if [ ! -f "$SNAPSHOT_HISTORY" ] ; then
    # Need an actual line for sed insert to work.
    echo | sudo tee "$SNAPSHOT_HISTORY" > /dev/null
  fi
}

tumbleweed_history()
{
  if [ $# -eq 1 ] ; then
    if [ "$(head -n1 "$SNAPSHOT_HISTORY")" != "$1" ] ; then
      sudo sed -i "1i$1" "$SNAPSHOT_HISTORY"
    fi
  else
    # Exclude empty lines necessary for data structure.
    grep -v -e '^$' "$SNAPSHOT_HISTORY"
  fi
}

tumbleweed_history_pop()
{
  if [ $# -eq 1 ] ; then
    sudo sed -i "1d" "$SNAPSHOT_HISTORY"
  else
    head -n2 "$SNAPSHOT_HISTORY" | tail -n1
  fi
}

tumbleweed_status()
{
  echo "latest   : $(tumbleweed_latest)"
  echo "target   : $(tumbleweed_target)"
  echo "installed: $(tumbleweed_installed)"
}

tumbleweed_latest()
{
  curl --silent "$URL/latest"
}

tumbleweed_target()
{
  tumbleweed_variable
}

tumbleweed_installed()
{
  cat /etc/os-release | grep -oP "VERSION_ID=\"\K(\d+)"
}

tumbleweed_list()
{
  curl --silent "$URL/list"
}

tumbleweed_update()
{
  tumbleweed_switch
  install=1
}

tumbleweed_prompt()
{
  echo -n "$1 [y/n] (y): "
  local response
  read response
  if [ "$response" != "" ] && [ "$response" != "y" ] ; then
    echo "exiting"
    exit 1
  fi
}

tumbleweed_switch()
{
  if [ $# -eq 1 ] ; then
    local version="$1"
    if ! tumbleweed_list | grep -Fx "$version" > /dev/null ; then
      echo "invalid version $version, not in available list"
      return
    fi
  else
    local version="$(tumbleweed_latest)"
    echo "choosing latest version"
  fi

  if [ "$version" == "$(tumbleweed_target)" ] && [ "$force" != "1" ] ; then
    echo "already on $version"
    exit
  fi

  tumbleweed_prompt "switching from $(tumbleweed_target) to $version?"
  tumbleweed_sudo
  tumbleweed_variable "$version"
}

tumbleweed_revert()
{
  local version="$(tumbleweed_history_pop)"
  if [ "$version" == "" ] ; then
    echo "no previous version in history"
    exit 1
  fi

  tumbleweed_prompt "switching from $(tumbleweed_target) to $version?"
  tumbleweed_sudo
  tumbleweed_history_pop "indeed"
  tumbleweed_variable "$version"
}

tumbleweed_uninit()
{
  if [ ! -d "$REPOS_DIR/.previous" ] ; then
    echo "nothing to revert"
    exit 1
  fi

  # Prompt for confirmation.
  local repos=($(ls "$REPOS_DIR/.previous"))
  echo -n "revert ${#repos[@]} repos? [y/n] (y): "
  read response
  if [ "$response" != "" ] && [ "$response" != "y" ] ; then
    echo "exiting"
    exit 1
  fi

  tumbleweed_sudo
  sudo mv "$REPOS_DIR/.previous"/* $REPOS_DIR/
  sudo rm -r "$REPOS_DIR/.previous"
  sudo rm "$VAR_FILE"
}

tumbleweed_install()
{
  sudo zypper ref
  sudo zypper dup
}

tumbleweed_usage()
{
  cat <<_EOF_
Usage: $0 [options] command [arguments]

Options:
    --version         Print version string and exit
    --force           Force on operation to occur regardless of checks.
    --install         Initiate install after command.
-h, --help            Display this message and exit

Commands:
init                  Initialize repos to point to snapshot repos.
status                Show status information (latest, target, and installed).
latest                Show latest snapshot available.
installed|version     Show current installed snapshot.
target                Show the target of the repositories.
list                  List available snapshots.
history               List history of snapshots targetted.
update|upgrade        Switch to and install the latest available snapshot.
switch %version       Switch to a new snapshot (none for latest).
revert                Revert to the previous snapshot or repo state.
uninit                Revert back to a snapshotless repository setup.
_EOF_
}

tumbleweed_handle()
{
  case "$1" in
    --version) echo "$VERSION" ; exit 0 ; ;;
    --force) force=1 ; ;;
    --install) install=1 ; ;;
    -h|--help) command="usage" ; ;;
    history|init|installed|latest|list|revert|status|target|uninit|update)
      command="$1" ; ;;
    version)
      command="installed" ; ;;
    upgrade)
      command="update" ; ;;
    switch)
      command="$1" ; args_expected=1 ;
      ;;
    -*) echo "unknown option $1" ; exit 1 ; ;;
    *)
      if [ $args_expected -eq 0 ] ; then
        return 1
      fi
      args+=("$1")
      ((args_expected--))
      ;;
  esac
  return 0
}

command="usage"
args_expected=0
args=()
while tumbleweed_handle $1 ; do
  shift
done

if [ "$command" != "usage" ] && [ "$command" != "init" ] && ! tumbleweed_inited ; then
  echo "repositories have not been initialized for snapshots"
  echo "  Try $0 init"
  exit 1
fi

tumbleweed_$command ${args[@]}

if [ "$install" == "1" ] ; then
  tumbleweed_install
fi
