#!/usr/bin/env bash
# shellcheck disable=SC2145,SC2178,SC2120,SC2162

# Functions to manage .env files
__dotenv=
__dotenv_file=
__dotenv_cmd=.env

.env() {
  REPLY=()
  [[ $__dotenv_file || ${1-} == -* ]] || .env.--file .env || return
  if declare -F -- ".env.${1-}" >/dev/null; then
    .env."$@"
    return
  fi
  return 64
}

.env.-f() { .env.--file "$@"; }

.env.get() {
  .env::arg "get requires a key" "$@" &&
    [[ "$__dotenv" =~ ^(.*(^|$'\n'))([ ]*)"$1="(.*)$ ]] &&
    REPLY=${BASH_REMATCH[4]%%$'\n'*} && REPLY=${REPLY%"${REPLY##*[![:space:]]}"}
}

.env.parse() {
  local line key
  while IFS= read -r line; do
    line=${line#"${line%%[![:space:]]*}"} # trim leading whitespace
    line=${line%"${line##*[![:space:]]}"} # trim trailing whitespace
    if [[ ! "$line" || "$line" == '#'* ]]; then continue; fi
    if (($#)); then
      for key; do
        if [[ $key == "${line%%=*}" ]]; then
          REPLY+=("$line")
          break
        fi
      done
    else
      REPLY+=("$line")
    fi
  done <<<"$__dotenv"
  ((${#REPLY[@]}))
}

.env.export() { ! .env.parse "$@" || export "${REPLY[@]}"; }

.env.set() {
  .env::file load || return
  local key saved=$__dotenv
  while (($#)); do
    key=${1#+}
    key=${key%%=*}
    if .env.get "$key"; then
      REPLY=()
      if [[ $1 == +* ]]; then
        shift
        continue # skip if already found
      elif [[ $1 == *=* ]]; then
        __dotenv=${BASH_REMATCH[1]}${BASH_REMATCH[3]}$1$'\n'${BASH_REMATCH[4]#*$'\n'}
      else
        __dotenv=${BASH_REMATCH[1]}${BASH_REMATCH[4]#*$'\n'}
        continue # delete all occurrences
      fi
    elif [[ $1 == *=* ]]; then
      __dotenv+="${1#+}"$'\n'
    fi
    shift
  done
  [[ $__dotenv == "$saved" ]] || .env::file save
}

.env.puts() { echo "${1-}" >>"$__dotenv_file" && __dotenv+="$1"$'\n'; }

.env.generate() {
  .env::arg "key required for generate" "$@" || return
  .env.get "$1" && return || REPLY=$("${@:2}") || return
  .env::one "generate: ouptut of '${*:2}' has more than one line" "$REPLY" || return
  .env.puts "$1=$REPLY"
}

.env.--file() {
  .env::arg "filename required for --file" "$@" || return
  __dotenv_file=$1
  .env::file load || return
  (($# < 2)) || .env "${@:2}"
}

.env::arg() { [[ "${2-}" ]] || {
  echo "$__dotenv_cmd: $1" >&2
  return 64
}; }

.env::one() { [[ "$2" != *$'\n'* ]] || .env::arg "$1"; }

.env::file() {
  local REPLY=$__dotenv_file
  case "$1" in
  load)
    __dotenv=
    ! [[ -f "$REPLY" ]] || __dotenv="$(<"$REPLY")"$'\n' || return
    ;;
  save)
    if [[ -L "$REPLY" ]] && declare -F -- realpath.resolved >/dev/null; then
      realpath.resolved "$REPLY"
    fi
    { [[ ! -f "$REPLY" ]] || cp -p "$REPLY" "$REPLY.bak"; } &&
      printf %s "$__dotenv" >"$REPLY.bak" && mv "$REPLY.bak" "$REPLY"
    ;;
  esac
}

# Shortcut to convert semver version (x.yyy.zzz) into a comparable number.
version-number() {
  echo "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'
}

# Get the current release channel for AzuraCast
get-release-channel() {
  local AZURACAST_VERSION="latest"
  if [[ -f .env ]]; then
    .env --file .env get AZURACAST_VERSION
    AZURACAST_VERSION="${REPLY:-latest}"
  fi

  echo "$AZURACAST_VERSION"
}

get-release-branch-name() {
  if [[ $(get-release-channel) == "stable" ]]; then
    echo "stable"
  else
    echo "main"
  fi
}

# This is a general-purpose function to ask Yes/No questions in Bash, either
# with or without a default answer. It keeps repeating the question until it
# gets a valid answer.
ask() {
  # https://djm.me/ask
  local prompt default reply

  while true; do

    if [[ "${2:-}" == "Y" ]]; then
      prompt="Y/n"
      default=Y
    elif [[ "${2:-}" == "N" ]]; then
      prompt="y/N"
      default=N
    else
      prompt="y/n"
      default=
    fi

    # Ask the question (not using "read -p" as it uses stderr not stdout)
    echo -n "$1 [$prompt] "

    read reply

    # Default?
    if [[ -z "$reply" ]]; then
      reply=${default}
    fi

    # Check if the reply is valid
    case "$reply" in
    Y* | y*) return 0 ;;
    N* | n*) return 1 ;;
    esac

  done
}

# Generate a prompt to set an environment file value.
envfile-set() {
  local VALUE INPUT

  .env --file .env

  .env get "$1"
  VALUE=${REPLY:-$2}

  echo -n "$3 [$VALUE]: "
  read INPUT

  VALUE=${INPUT:-$VALUE}

  .env set "${1}=${VALUE}"
}

#
# Configure the ports used by AzuraCast.
#
setup-ports() {
  envfile-set "AZURACAST_HTTP_PORT" "80" "Port to use for HTTP connections"
  envfile-set "AZURACAST_HTTPS_PORT" "443" "Port to use for HTTPS connections"
  envfile-set "AZURACAST_SFTP_PORT" "2022" "Port to use for SFTP connections"
}

#
# Configure release mode settings.
#
setup-release() {
  if [[ ! -f .env ]]; then
    curl -fsSL https://raw.githubusercontent.com/AzuraCast/AzuraCast/main/sample.env -o .env
  fi

  local OLD_RELEASE_CHANNEL
  .env --file .env get AZURACAST_VERSION
  OLD_RELEASE_CHANNEL="${REPLY:-latest}"

  local AZURACAST_VERSION="${OLD_RELEASE_CHANNEL}"

  if [[ $AZURACAST_VERSION == "latest" ]]; then
    if ask "Your current release channel is 'Rolling Release'. Switch to 'Stable' release channel?" N; then
      AZURACAST_VERSION="stable"
    fi
  elif [[ $AZURACAST_VERSION == "stable" ]]; then
    if ask "Your current release channel is 'Stable'. Switch to 'Rolling Release' release channel?" N; then
      AZURACAST_VERSION="latest"
    fi
  fi

  .env --file .env set AZURACAST_VERSION=${AZURACAST_VERSION}

  if [[ $AZURACAST_VERSION != $OLD_RELEASE_CHANNEL ]]; then
    if ask "You should update the Docker Utility Script after changing release channels. Automatically update it now?" Y; then
      update-self
    fi
  fi
}

check-install-requirements() {
  local CURRENT_OS CURRENT_ARCH REQUIRED_COMMANDS SCRIPT_DIR

  set -e

  echo "Checking installation requirements for AzuraCast..."

  CURRENT_OS=$(uname -s)
  if [[ $CURRENT_OS == "Linux" ]]; then
    echo -en "\e[32m[PASS]\e[0m Operating System: ${CURRENT_OS}\n"
  else
    echo -en "\e[41m[FAIL]\e[0m Operating System: ${CURRENT_OS}\n"

    echo "       You are running an unsupported operating system."
    echo "       Automated AzuraCast installation is not currently supported on this"
    echo "       operating system."
    exit 1
  fi

  CURRENT_ARCH=$(uname -m)
  if [[ $CURRENT_ARCH == "x86_64" ]]; then
    echo -en "\e[32m[PASS]\e[0m Architecture: ${CURRENT_ARCH}\n"
  elif [[ $CURRENT_ARCH == "aarch64" ]]; then
    echo -en "\e[32m[PASS]\e[0m Architecture: ${CURRENT_ARCH}\n"
  else
    echo -en "\e[41m[FAIL]\e[0m Architecture: ${CURRENT_ARCH}\n"

    echo "       You are running an unsupported processor architecture."
    echo "       Automated AzuraCast installation is not currently supported on this "
    echo "       operating system."
    exit 1
  fi

  REQUIRED_COMMANDS=(curl awk)
  for COMMAND in "${REQUIRED_COMMANDS[@]}" ; do
    if [[ $(command -v "$COMMAND") ]]; then
      echo -en "\e[32m[PASS]\e[0m Command Present: ${COMMAND}\n"
    else
      echo -en "\e[41m[FAIL]\e[0m Command Present: ${COMMAND}\n"

      echo "       ${COMMAND} does not appear to be installed."
      echo "       Install ${COMMAND} using your host's package manager,"
      echo "       then continue installing using this script."
      exit 1
    fi
  done

  if [[ $EUID -ne 0 ]]; then
    if [[ $(command -v sudo) ]]; then
      echo -en "\e[32m[PASS]\e[0m User Permissions\n"
    else
      echo -en "\e[41m[FAIL]\e[0m User Permissions\n"

      echo "       You are not currently the root user, and "
      echo "       'sudo' does not appear to be installed."
      echo "       Install sudo using your host's package manager,"
      echo "       then continue installing using this script."
      exit 1
    fi
  else
    echo -en "\e[32m[PASS]\e[0m User Permissions\n"
  fi

  SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
  if [[ $SCRIPT_DIR == "/var/azuracast" ]]; then
    echo -en "\e[32m[PASS]\e[0m Installation Directory\n"
  else
    echo -en "\e[93m[WARN]\e[0m Installation Directory\n"
    echo "       AzuraCast is not installed in /var/azuracast, as is recommended"
    echo "       for most installations. This will not prevent AzuraCast from"
    echo "       working, but you will need to update any instructions in our"
    echo "       documentation to reflect your current directory:"
    echo "       $SCRIPT_DIR"
  fi

  echo -en "\e[32m[PASS]\e[0m All requirements met!\n"

  set +e
}

install-docker() {
  set -e

  curl -fsSL get.docker.com -o get-docker.sh
  sh get-docker.sh
  rm get-docker.sh

  if [[ $EUID -ne 0 ]]; then
    sudo usermod -aG docker "$(whoami)"

    echo "You must log out or restart to apply necessary Docker permissions changes."
    echo "Restart, then continue installing using this script."
    exit 1
  fi

  set +e
}

install-docker-compose() {
  set -e
  echo "Installing Docker Compose..."

  curl -fsSL -o docker-compose https://github.com/docker/compose/releases/download/v2.4.1/docker-compose-linux-$(uname -m)

  ARCHITECTURE=amd64
  if [ "$(uname -m)" = "aarch64" ]; then
    ARCHITECTURE=arm64
  fi
  curl -fsSL -o docker-compose-switch https://github.com/docker/compose-switch/releases/download/v1.0.4/docker-compose-linux-${ARCHITECTURE}

  if [[ $EUID -ne 0 ]]; then
    sudo chmod a+x ./docker-compose
    sudo chmod a+x ./docker-compose-switch

    sudo mv ./docker-compose /usr/libexec/docker/cli-plugins/docker-compose
    sudo mv ./docker-compose-switch /usr/local/bin/docker-compose
  else
    chmod a+x ./docker-compose
    chmod a+x ./docker-compose-switch

    mv ./docker-compose /usr/libexec/docker/cli-plugins/docker-compose
    mv ./docker-compose-switch /usr/local/bin/docker-compose
  fi

  echo "Docker Compose updated!"
  set +e
}

run-installer() {
  local AZURACAST_RELEASE_BRANCH
  AZURACAST_RELEASE_BRANCH=$(get-release-branch-name)

  if [[ ! -f .env ]]; then
    curl -fsSL https://raw.githubusercontent.com/AzuraCast/AzuraCast/$AZURACAST_RELEASE_BRANCH/sample.env -o .env
  fi
  if [[ ! -f azuracast.env ]]; then
    curl -fsSL https://raw.githubusercontent.com/AzuraCast/AzuraCast/$AZURACAST_RELEASE_BRANCH/azuracast.sample.env -o azuracast.env
  fi
  if [[ ! -f docker-compose.yml ]]; then
    curl -fsSL https://raw.githubusercontent.com/AzuraCast/AzuraCast/$AZURACAST_RELEASE_BRANCH/docker-compose.sample.yml -o docker-compose.yml
  fi

  touch docker-compose.new.yml

  local dc_config_test=$(docker-compose -f docker-compose.new.yml config 2>/dev/null)
  if [ $? -ne 0 ]; then
    if ask "Docker Compose needs to be updated to continue. Update to latest version?" Y; then
      install-docker-compose
    fi
  fi

  curl -fsSL https://raw.githubusercontent.com/AzuraCast/AzuraCast/$AZURACAST_RELEASE_BRANCH/docker-compose.installer.yml -o docker-compose.installer.yml

  docker-compose -p azuracast_installer -f docker-compose.installer.yml pull
  docker-compose -p azuracast_installer -f docker-compose.installer.yml run --rm installer install "$@"

  rm docker-compose.installer.yml
}

#
# Run the initial installer of Docker and AzuraCast.
# Usage: ./docker.sh install
#
install() {
  check-install-requirements

  if [[ $(command -v docker) && $(docker --version) ]]; then
    echo "Docker is already installed! Continuing..."
  else
    if ask "Docker does not appear to be installed. Install Docker now?" Y; then
      install-docker
    fi
  fi

  if [[ $(command -v docker-compose) ]]; then
    echo "Docker Compose is already installed. Continuing..."
  else
    if ask "Docker Compose does not appear to be installed. Install Docker Compose now?" Y; then
      install-docker-compose
    fi
  fi

  setup-release

  run-installer "$@"

  # Installer creates a file at docker-compose.new.yml; copy it to the main spot.
  if [[ -s docker-compose.new.yml ]]; then
    if [[ -f docker-compose.yml ]]; then
      rm docker-compose.yml
    fi

    mv docker-compose.new.yml docker-compose.yml
  fi

  # If this script is running as a non-root user, set the PUID/PGID in the environment vars appropriately.
  if [[ $EUID -ne 0 ]]; then
    .env --file .env set AZURACAST_PUID="$(id -u)"
    .env --file .env set AZURACAST_PGID="$(id -g)"
  fi

  docker-compose pull

  docker-compose run --rm web -- azuracast_install "$@"
  docker-compose up -d
  exit
}

install-dev() {
  if [[ $(command -v docker) && $(docker --version) ]]; then
    echo "Docker is already installed! Continuing..."
  else
    if ask "Docker does not appear to be installed. Install Docker now?" Y; then
      install-docker
    fi
  fi

  if [[ $(command -v docker-compose) ]]; then
    echo "Docker Compose is already installed. Continuing..."
  else
    if ask "Docker Compose does not appear to be installed. Install Docker Compose now?" Y; then
      install-docker-compose
    fi
  fi

  if [[ ! -f docker-compose.yml ]]; then
    cp docker-compose.sample.yml docker-compose.yml
  fi
  if [[ ! -f docker-compose.override.yml ]]; then
    cp docker-compose.dev.yml docker-compose.override.yml
  fi
  if [[ ! -f .env ]]; then
    cp dev.env .env
  fi
  if [[ ! -f azuracast.env ]]; then
    cp azuracast.dev.env azuracast.env

    echo "Customize azuracast.env file now before continuing. Re-run this command to continue installation."
    exit
  fi

  # If this script is running as a non-root user, set the PUID/PGID in the environment vars appropriately.
  if [[ $EUID -ne 0 ]]; then
    .env --file .env set AZURACAST_PUID="$(id -u)"
    .env --file .env set AZURACAST_PGID="$(id -g)"
  fi

  chmod 777 ./frontend/ ./web/ ./vendor/ \
    ./web/static/ ./web/static/api/ \
    ./web/static/dist/ ./web/static/img/

  docker-compose build
  docker-compose run --rm web -- azuracast_install "$@"

  docker-compose -p azuracast_frontend -f docker-compose.frontend.yml build
  docker-compose -p azuracast_frontend -f docker-compose.frontend.yml run --rm frontend npm run build

  docker-compose up -d
  exit
}

#
# Update the Docker images and codebase.
# Usage: ./docker.sh update
#
update() {
  echo "[NOTICE] Before you continue, please make sure you have a recent snapshot of your system and or backed it up."
  if ask "Are you ready to continue with the update?" Y; then

    # Check for a new Docker Utility Script.
    local AZURACAST_RELEASE_BRANCH
    AZURACAST_RELEASE_BRANCH=$(get-release-branch-name)

    curl -fsSL https://raw.githubusercontent.com/AzuraCast/AzuraCast/$AZURACAST_RELEASE_BRANCH/docker.sh -o docker.new.sh

    local UTILITY_FILES_MATCH
    UTILITY_FILES_MATCH="$(
      cmp --silent docker.sh docker.new.sh
      echo $?
    )"

    local UPDATE_UTILITY=0
    if [[ ${UTILITY_FILES_MATCH} -ne 0 ]]; then
      if ask "The Docker Utility Script has changed since your version. Update to latest version?" Y; then
        UPDATE_UTILITY=1
      fi
    fi

    if [[ ${UPDATE_UTILITY} -ne 0 ]]; then
      mv docker.new.sh docker.sh
      chmod a+x docker.sh

      echo "A new Docker Utility Script has been downloaded."
      echo "Please re-run the update process to continue."
      exit
    else
      rm docker.new.sh
    fi

    run-installer --update "$@"

    # Check for updated Docker Compose config.
    local COMPOSE_FILES_MATCH

    if [[ ! -s docker-compose.new.yml ]]; then
      curl -fsSL https://raw.githubusercontent.com/AzuraCast/AzuraCast/$AZURACAST_RELEASE_BRANCH/docker-compose.sample.yml -o docker-compose.new.yml
    fi

    COMPOSE_FILES_MATCH="$(
      cmp --silent docker-compose.yml docker-compose.new.yml
      echo $?
    )"

    if [[ ${COMPOSE_FILES_MATCH} -ne 0 ]]; then
      docker-compose -f docker-compose.new.yml pull
      docker-compose down

      cp docker-compose.yml docker-compose.backup.yml
      mv docker-compose.new.yml docker-compose.yml
    else
      rm docker-compose.new.yml

      docker-compose pull
      docker-compose down
    fi

    docker-compose run --rm web -- azuracast_update "$@"
    docker-compose up -d

    if ask "Clean up all stopped Docker containers and images to save space?" Y; then
      docker system prune -f
    fi

    echo "Update complete!"
  fi
  exit
}

#
# Update this Docker utility script.
# Usage: ./docker.sh update-self
#
update-self() {
  local AZURACAST_RELEASE_BRANCH
  AZURACAST_RELEASE_BRANCH=$(get-release-branch-name)

  curl -fsSL https://raw.githubusercontent.com/AzuraCast/AzuraCast/$AZURACAST_RELEASE_BRANCH/docker.sh -o docker.sh
  chmod a+x docker.sh

  echo "New Docker utility script downloaded."
  exit
}

#
# Run a CLI command inside the Docker container.
# Usage: ./docker.sh cli [command]
#
cli() {
  docker-compose exec --user="azuracast" web azuracast_cli "$@"
  exit
}

#
# Enter the bash terminal of the running web container.
# Usage: ./docker.sh bash
#
bash() {
  docker-compose exec --user="azuracast" web bash
  exit
}

#
# Enter the MariaDB database management terminal with the correct credentials.
#
db() {
  local MYSQL_HOST MYSQL_PORT MYSQL_USER MYSQL_PASSWORD MYSQL_DATABASE

  .env --file azuracast.env get MYSQL_HOST
  MYSQL_HOST="${REPLY:-localhost}"

  .env --file azuracast.env get MYSQL_PORT
  MYSQL_PORT="${REPLY:-3306}"

  .env --file azuracast.env get MYSQL_USER
  MYSQL_USER="${REPLY:-azuracast}"

  .env --file azuracast.env get MYSQL_PASSWORD
  MYSQL_PASSWORD="${REPLY:-azur4c457}"

  .env --file azuracast.env get MYSQL_DATABASE
  MYSQL_DATABASE="${REPLY:-azuracast}"

  docker-compose exec --user="mysql" web mysql --user=${MYSQL_USER} --password=${MYSQL_PASSWORD} \
    --host=${MYSQL_HOST} --port=${MYSQL_PORT} --database=${MYSQL_DATABASE}

  exit
}

#
# Back up the Docker volumes to a .tar.gz file.
# Usage:
# ./docker.sh backup [/custom/backup/dir/custombackupname.zip]
#
backup() {
  local BACKUP_PATH BACKUP_DIR BACKUP_FILENAME BACKUP_EXT
  BACKUP_PATH=$(readlink -f ${1:-"./backup.tar.gz"})
  BACKUP_DIR=$(dirname -- "$BACKUP_PATH")
  BACKUP_FILENAME=$(basename -- "$BACKUP_PATH")
  BACKUP_EXT="${BACKUP_FILENAME##*.}"
  shift

  # Prepare permissions
  if [[ $EUID -ne 0 ]]; then
    .env --file .env set AZURACAST_PUID="$(id -u)"
    .env --file .env set AZURACAST_PGID="$(id -g)"
  fi

  docker-compose exec --user="azuracast" web azuracast_cli azuracast:backup "/var/azuracast/backups/${BACKUP_FILENAME}" "$@"

  # Move from Docker volume to local filesystem
  docker run --rm -v "azuracast_backups:/backup_src" \
  -v "$BACKUP_DIR:/backup_dest" \
  busybox mv "/backup_src/${BACKUP_FILENAME}" "/backup_dest/${BACKUP_FILENAME}"
}

#
# Restore an AzuraCast backup into Docker.
# Usage:
# ./docker.sh restore [/custom/backup/dir/custombackupname.zip]
#
restore() {
  if [[ ! -f .env ]] || [[ ! -f azuracast.env ]]; then
    echo "AzuraCast hasn't been installed yet on this server."
    echo "You should run './docker.sh install' first before restoring."
    exit 1
  fi

  if ask "Restoring will remove any existing AzuraCast installation data, replacing it with your backup. Continue?" Y; then
    if [[ $1 != "" ]]; then
      local BACKUP_PATH BACKUP_DIR BACKUP_FILENAME BACKUP_EXT
      BACKUP_PATH=$(readlink -f ${1:-"./backup.tar.gz"})
      BACKUP_DIR=$(dirname -- "$BACKUP_PATH")
      BACKUP_FILENAME=$(basename -- "$BACKUP_PATH")
      BACKUP_EXT="${BACKUP_FILENAME##*.}"
      shift

      if [[ ! -f ${BACKUP_PATH} ]]; then
        echo "File '${BACKUP_PATH}' does not exist. Nothing to restore."
        exit 1
      fi

      docker-compose down -v
      docker volume create azuracast_backups

      # Move from local filesystem to Docker volume
      docker run --rm -v "$BACKUP_DIR:/backup_src" \
        -v "azuracast_backups:/backup_dest" \
        busybox mv "/backup_src/${BACKUP_FILENAME}" "/backup_dest/${BACKUP_FILENAME}"

      # Prepare permissions
      if [[ $EUID -ne 0 ]]; then
        .env --file .env set AZURACAST_PUID="$(id -u)"
        .env --file .env set AZURACAST_PGID="$(id -g)"
      fi

      docker-compose run --rm web -- azuracast_restore "/var/azuracast/backups/${BACKUP_FILENAME}" "$@"

      # Move file back from volume to local filesystem
      docker run --rm -v "azuracast_backups:/backup_src" \
        -v "$BACKUP_DIR:/backup_dest" \
        busybox mv "/backup_src/${BACKUP_FILENAME}" "/backup_dest/${BACKUP_FILENAME}"

      docker-compose down
      docker-compose up -d
    else
      docker-compose down

      # Remove all volumes except the backup volume.
      docker volume rm -f $(docker volume ls | grep -v "azuracast_backups" | awk 'NR>1 {print $2}')

      docker-compose run --rm web -- azuracast_restore "$@"

      docker-compose down
      docker-compose up -d
    fi
  fi
  exit
}

#
# Restore the Docker volumes from a legacy backup format .tar.gz file.
# Usage:
# ./docker.sh restore [/custom/backup/dir/custombackupname.tar.gz]
#
restore-legacy() {
  local APP_BASE_DIR BACKUP_PATH BACKUP_DIR BACKUP_FILENAME

  APP_BASE_DIR=$(pwd)

  BACKUP_PATH=${1:-"./backup.tar.gz"}
  BACKUP_DIR=$(cd "$(dirname "$BACKUP_PATH")" && pwd)
  BACKUP_FILENAME=$(basename "$BACKUP_PATH")

  cd "$APP_BASE_DIR" || exit

  if [ -f "$BACKUP_PATH" ]; then
    docker-compose down

    docker volume rm azuracast_db_data azuracast_station_data
    docker volume create azuracast_db_data
    docker volume create azuracast_station_data

    docker run --rm -v "$BACKUP_DIR:/backup" \
      -v azuracast_db_data:/azuracast/db \
      -v azuracast_station_data:/azuracast/stations \
      busybox tar zxvf "/backup/$BACKUP_FILENAME"

    docker-compose up -d
  else
    echo "File $BACKUP_PATH does not exist in this directory. Nothing to restore."
    exit 1
  fi

  exit
}

#
# DEVELOPER TOOL:
# Access the static console as a developer.
# Usage: ./docker.sh static [static_container_command]
#
static() {
  docker-compose -f docker-compose.frontend.yml down -v
  docker-compose -f docker-compose.frontend.yml build
  docker-compose --env-file=.env -f docker-compose.frontend.yml run --rm frontend "$@"
  exit
}

#
# Stop all Docker containers and remove related volumes.
# Usage: ./docker.sh uninstall
#
uninstall() {
  if ask "This operation is destructive and will wipe your existing Docker containers. Continue?" N; then

    docker-compose down -v
    docker-compose rm -f
    docker volume prune -f

    echo "All AzuraCast Docker containers and volumes were removed."
    echo "To remove *all* Docker containers and volumes, run:"
    echo "  docker stop \$(docker ps -a -q)"
    echo "  docker rm \$(docker ps -a -q)"
    echo "  docker volume prune -f"
    echo ""
  fi

  exit
}

#
# LetsEncrypt: Now managed via the Web UI.
#
setup-letsencrypt() {
  echo "LetsEncrypt is now managed from within the web interface."
}

letsencrypt-create() {
  setup-letsencrypt
  exit
}

#
# Utility script to facilitate switching ports.
# Usage: ./docker.sh change-ports
#
change-ports() {
  setup-ports

  docker-compose down
  docker-compose up -d
}

#
# Helper scripts for basic Docker Compose functions
#
up() {
  echo "Starting up AzuraCast services..."
  docker-compose up -d
}

down() {
  echo "Shutting down AzuraCast services..."
  docker-compose down
}

restart() {
  down
  up
}

# Ensure we're in the same directory as this script.
cd "$( dirname "${BASH_SOURCE[0]}" )" || exit

"$@"
