#!/usr/bin/env bash
set -eo pipefail
shopt -s nullglob

export DOCKER_BIN=docker
if [[ -r /etc/default/dokku ]]; then
  source /etc/default/dokku
fi

export DOKKU_ROOT=${DOKKU_ROOT:=~dokku}
if [[ -f "$DOKKU_ROOT/dokkurc" ]]; then
  if [[ -r $DOKKU_ROOT/dokkurc ]]; then
    source "$DOKKU_ROOT/dokkurc"
  else
    echo "Unable to read $DOKKU_ROOT/dokkurc for sourcing" 1>&2
    exit 1
  fi
fi
if [[ -d $DOKKU_ROOT/.dokkurc ]]; then
  for f in $DOKKU_ROOT/.dokkurc/*; do
    if [[ -r "$f" ]]; then
      source "$f"
    else
      echo "Unable to read $f for sourcing" 1>&2
      exit 1
    fi
  done
fi
[[ $DOKKU_TRACE ]] && set -x
export DOKKU_HOST_ROOT=${DOKKU_HOST_ROOT:=$DOKKU_ROOT}

export DOKKU_DISTRO
DOKKU_DISTRO=$(
  . /etc/os-release &>/dev/null || true
  echo "$ID"
)

# Specify twice so that only host-level
# configuration can ever override the DOCKER_BIN value
export DOCKER_BIN=${DOCKER_BIN:="docker"}

export DOKKU_IMAGE=${DOKKU_IMAGE:="gliderlabs/herokuish:latest-24"}
export DOKKU_CNB_BUILDER=${DOKKU_CNB_BUILDER:="heroku/builder:24"}
export DOKKU_LIB_ROOT=${DOKKU_LIB_PATH:="/var/lib/dokku"}

export PLUGIN_PATH=${PLUGIN_PATH:="$DOKKU_LIB_ROOT/plugins"}
export PLUGIN_AVAILABLE_PATH=${PLUGIN_AVAILABLE_PATH:="$PLUGIN_PATH/available"}
export PLUGIN_ENABLED_PATH=${PLUGIN_ENABLED_PATH:="$PLUGIN_PATH/enabled"}
export PLUGIN_CORE_PATH=${PLUGIN_CORE_PATH:="$DOKKU_LIB_ROOT/core-plugins"}
export PLUGIN_CORE_AVAILABLE_PATH=${PLUGIN_CORE_AVAILABLE_PATH:="$PLUGIN_CORE_PATH/available"}
export PLUGIN_CORE_ENABLED_PATH=${PLUGIN_CORE_ENABLED_PATH:="$PLUGIN_CORE_PATH/enabled"}
export DOKKU_SYSTEM_GROUP=${DOKKU_SYSTEM_GROUP:="dokku"}
export DOKKU_SYSTEM_USER=${DOKKU_SYSTEM_USER:="dokku"}

export DOKKU_API_VERSION=1
export DOKKU_NOT_IMPLEMENTED_EXIT=10
export DOKKU_VALID_EXIT=0
export DOKKU_PID="${DOKKU_PID:=$$}"

export DOKKU_LOGS_DIR=${DOKKU_LOGS_DIR:="/var/log/dokku"}
export DOKKU_LOGS_HOST_DIR=${DOKKU_LOGS_HOST_DIR:=$DOKKU_LOGS_DIR}
export DOKKU_EVENTS_LOGFILE=${DOKKU_EVENTS_LOGFILE:="$DOKKU_LOGS_DIR/events.log"}

export DOKKU_CONTAINER_LABEL=dokku
export DOKKU_GLOBAL_BUILD_ARGS="--label=org.label-schema.schema-version=1.0 --label=org.label-schema.vendor=dokku --label=$DOKKU_CONTAINER_LABEL"
export DOKKU_GLOBAL_RUN_ARGS="--label=org.label-schema.schema-version=1.0 --label=org.label-schema.vendor=dokku --label=$DOKKU_CONTAINER_LABEL"

source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"

parse_args "$@"
args=("$@")
skip_arg=false
if [[ "${args[0]}" =~ ^--.* ]]; then
  for arg in "$@"; do
    $skip_arg && skip_arg=false && continue
    if [[ "$arg" == "--version" ]]; then
      continue
    fi
    if [[ "$arg" == "--app" ]]; then
      shift 2 && skip_arg=true
    elif [[ "$arg" =~ ^--.* ]]; then
      shift 1
    else
      break
    fi
  done
fi
! has_tty && DOKKU_QUIET_OUTPUT=1

if [[ $(id -un) != "dokku" ]]; then
  if [[ ! $1 =~ plugin:* ]] && [[ $1 != "ssh-keys:add" ]] && [[ $1 != "ssh-keys:remove" ]] && [[ $1 != "scheduler-k3s:initialize" ]] && [[ $1 != "scheduler-k3s:uninstall" ]]; then
    unset DOKKU_PID TMP TMPDIR TEMP TEMPDIR
    export SSH_USER=$(id -un)
    sudo -u dokku -E -H "$0" "$@"
    exit $?
  fi
fi

if [[ $1 =~ ^plugin:.* && $1 != "plugin:help" && $1 != "plugin:list" ]] || [[ $1 == "ssh-keys:add" ]] || [[ $1 == "ssh-keys:remove" ]] || [[ $1 == "scheduler-k3s:initialize" ]] || [[ $1 == "scheduler-k3s:uninstall" ]]; then
  if [[ $(id -un) != "root" ]]; then
    dokku_log_fail "This command must be run as root"
  else
    export SSH_USER=root
  fi
fi

if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; then
  export -n SSH_ORIGINAL_COMMAND
  exit_code=0
  if [[ $1 =~ git-* ]] || [[ $1 =~ git:* ]]; then
    set -f
    $0 $SSH_ORIGINAL_COMMAND
    exit_code=$?
    set +f
  else
    readarray -t -O "${#ssh_arg_array[@]}" ssh_arg_array < <(printf '%s' "$SSH_ORIGINAL_COMMAND" | xargs -n 1)
    set -f
    $0 "${ssh_arg_array[@]}"
    exit_code=$?
    set +f
  fi
  exit $exit_code
fi

if ! dokku_auth "$@"; then
  dokku_log_fail "Access denied"
  exit 1
fi

execute_dokku_cmd() {
  declare desc="executes dokku sub-commands"
  local PLUGIN_NAME="$1"
  local PLUGIN_CMD=$PLUGIN_NAME
  local implemented=0
  local script
  local argv=("$@")

  local -A PLUGIN_ALIASES
  PLUGIN_ALIASES["docker-local"]="scheduler-docker-local"
  PLUGIN_ALIASES["dockerfile"]="builder-dockerfile"
  PLUGIN_ALIASES["herokuish"]="builder-herokuish"
  PLUGIN_ALIASES["k3s"]="scheduler-k3s"
  PLUGIN_ALIASES["lambda"]="builder-lambda"
  PLUGIN_ALIASES["nixpacks"]="builder-nixpacks"
  PLUGIN_ALIASES["pack"]="builder-pack"
  PLUGIN_ALIASES["rails"]="builder-railpack"

  local plugin_alias_source="${PLUGIN_NAME%%:*}"
  local plugin_alias_target="${PLUGIN_ALIASES[$plugin_alias_source]}"
  if [[ -n "$plugin_alias_target" ]]; then
    PLUGIN_NAME="${PLUGIN_NAME/$plugin_alias_source/$plugin_alias_target}"
    PLUGIN_CMD="${PLUGIN_CMD/$plugin_alias_source/$plugin_alias_target}"
    # also replace the first argument in $@ with the new PLUGIN_NAME
    shift 1
    set -- "$PLUGIN_NAME" "$@"
    argv=("$PLUGIN_NAME" "${argv[@]:1}")
  fi

  case "$PLUGIN_NAME" in
    events | events:*)
      local PLUGIN_NAME=${PLUGIN_NAME/events/20_events}
      ;;
    caddy | caddy:*)
      local PLUGIN_NAME=${PLUGIN_NAME/caddy/caddy-vhosts}
      ;;
    haproxy | haproxy:*)
      local PLUGIN_NAME=${PLUGIN_NAME/haproxy/haproxy-vhosts}
      ;;
    nginx | nginx:*)
      local PLUGIN_NAME=${PLUGIN_NAME/nginx/nginx-vhosts}
      ;;
    openresty | openresty:*)
      local PLUGIN_NAME=${PLUGIN_NAME/openresty/openresty-vhosts}
      ;;
    traefik | traefik:*)
      local PLUGIN_NAME=${PLUGIN_NAME/traefik/traefik-vhosts}
      ;;
    deploy | cleanup | url | urls | report)
      local PLUGIN_NAME="00_dokku-standard"
      ;;
  esac

  if [[ "$(readlink -f "$PLUGIN_ENABLED_PATH/${PLUGIN_NAME%%:*}")" == *core-plugins* ]]; then
    [[ ${argv[0]} == "$PLUGIN_CMD" ]] && shift 1
    if [[ -n $DOKKU_APP_NAME ]]; then
      set -- "$DOKKU_APP_NAME" "$@"
    fi
    set -- "$PLUGIN_CMD" "$@"
  fi

  if [[ -x $PLUGIN_ENABLED_PATH/$PLUGIN_NAME/subcommands/default ]]; then
    "$PLUGIN_ENABLED_PATH/$PLUGIN_NAME/subcommands/default" "$@"
    implemented=1
  elif [[ -x $PLUGIN_ENABLED_PATH/$PLUGIN_NAME/subcommands/$PLUGIN_CMD ]]; then
    "$PLUGIN_ENABLED_PATH/$PLUGIN_NAME/subcommands/$PLUGIN_CMD" "$@"
    implemented=1
  elif [[ -x $PLUGIN_ENABLED_PATH/${PLUGIN_NAME%%:*}/subcommands/${1#*:} ]] && [[ -n "${1#*:}" ]]; then
    "$PLUGIN_ENABLED_PATH/${PLUGIN_NAME%%:*}/subcommands/${1#*:}" "$@"
    implemented=1
  fi

  if [[ $implemented -eq 0 ]]; then
    for script in $PLUGIN_ENABLED_PATH/*/commands; do
      set +e
      $script "$@"
      exit_code=$?
      set -e
      if [[ "$exit_code" -eq "$DOKKU_NOT_IMPLEMENTED_EXIT" ]]; then
        continue
      fi
      implemented=1
      if [[ "$exit_code" -ne "$DOKKU_VALID_EXIT" ]]; then
        exit $exit_code
      fi
    done
  fi

  if [[ "$implemented" -eq 0 ]]; then
    dokku_log_warn "\`$*\` is not a dokku command."
    dokku_log_warn "See \`dokku help\` for a list of available commands."
    exit 1
  fi
}

case "$1" in
  help | '')
    export LC_ALL=C # so sort will respect non alpha characters
    ALL_PLUGIN_COMMANDS=$(find -L "$PLUGIN_PATH/enabled" -name commands 2>/dev/null || true)

    # build list of core plugin command files
    for plugin_command in $ALL_PLUGIN_COMMANDS; do
      if [[ "$(readlink -f "$plugin_command")" == *core-plugins* ]]; then
        CORE_PLUGIN_COMMANDS+="$plugin_command "
      fi
    done
    # build list of non-core plugin command files
    for plugin_command in $ALL_PLUGIN_COMMANDS; do
      if [[ "$(readlink -f "$plugin_command")" != *core-plugins* ]]; then
        COMMUNITY_PLUGIN_COMMANDS+="$plugin_command "
      fi
    done

    # New lines are blank echos for readability
    dokku_log_quiet "Usage: dokku [--quiet|--trace|--force] COMMAND <app> [command-specific-options]"
    dokku_log_quiet ""
    dokku_log_quiet "Primary help options, type \"dokku COMMAND:help\" for more details, or dokku help --all to see all commands."
    dokku_log_quiet ""
    dokku_log_quiet "Commands:"
    dokku_log_quiet ""

    if [[ "$2" == "--all" ]]; then
      for core_plugin_command in $CORE_PLUGIN_COMMANDS; do
        $core_plugin_command help
      done | sort | column -c2 -t -s,
      dokku_log_quiet ""
      dokku_log_quiet "Community plugin commands:"
      dokku_log_quiet ""
      for community_plugin_command in $COMMUNITY_PLUGIN_COMMANDS; do
        $community_plugin_command help
      done | sort | column -c2 -t -s,
    else
      for core_plugin_command in $CORE_PLUGIN_COMMANDS; do
        $core_plugin_command help
        # When done, sort, sed cuts arguments out of strings and colum make it pretty
      done | sort | sed -e '/^.*:/d' -e 's/\s[\[\<\-\(].*,/,/' | column -c2 -t -s,
      if [[ -n "$COMMUNITY_PLUGIN_COMMANDS" ]]; then
        dokku_log_quiet ""
        dokku_log_quiet "Community plugin commands:"
        dokku_log_quiet ""
        for community_plugin_command in $COMMUNITY_PLUGIN_COMMANDS; do
          $community_plugin_command help
        done | sort | column -c2 -t -s,
      fi
    fi
    ;;

  version | -v | --version)
    dokku_version
    ;;

  *)
    execute_dokku_cmd "$@"
    ;;

esac
