#!/usr/bin/env bash

export DOKKU_ROOT=${DOKKU_ROOT:="/home/dokku"}
export PLUGIN_PATH=${PLUGIN_PATH:="/var/lib/dokku-alt/plugins"}
export DOKKU_NOT_IMPLEMENTED_EXIT=10
export DOKKU_VALID_EXIT=0

if [[ $(id -un) != "dokku" && $1 != "plugins-install" ]]; then
  sudo -u dokku -H NAME="$(id -un)" FINGERPRINT="shell" $0 "$@"
  exit
fi

if [[ $(id -un) != "dokku" && $1 != plugins-install* && $1 != "plugins-update" ]]; then
  sudo -u dokku -E -H NAME="$(id -un)" FINGERPRINT="shell" $0 "$@"
  exit
fi

if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; then
  export -n SSH_ORIGINAL_COMMAND
  $0 $SSH_ORIGINAL_COMMAND
  exit $?
fi

# it fixes some not nice docker errors
cd $DOKKU_ROOT

# locale
export LANGUAGE=${LANGUAGE:-en_US.UTF-8}
export LANG=${LANG:-en_US.UTF-8}
export LC_ALL=${LC_ALL:-en_US.UTF-8}

source "$PLUGIN_PATH/dokku_common"

[[ -f $DOKKU_ROOT/dokkurc ]] && source $DOKKU_ROOT/dokkurc

if [[ "$1" == "" ]]; then
  dokku help
  exit
fi

: | pluginhook verify-command "$@"

case "$1" in
  release)
    verify_app_name "$2"
    verify_max_args "2" "$@"

    if ! flock -n "$APP_BUILD_LOCK" true; then
      info2 "$APP is currently locked. We will wait for other process to finish."
    fi

    shift 1

    flock "$APP_BUILD_LOCK" dokku release:locked "$@"
    ;;

  release:locked)
    verify_app_name "$2"
    verify_max_args "2" "$@"

    if ! docker_image_exists "$IMAGE_GENERIC:build"; then
      info "Application ($IMAGE_GENERIC:build) is not yet build!"
      exit 1
    fi

    info "Releasing $APP ..."
    # use :latest tag to ensure compatiblity with orginal dokku
    tag_image "$IMAGE_GENERIC:build" "$IMAGE"
    if is_image_buildstep_based "$IMAGE"; then
      pluginhook pre-release "$APP"
      dokku config "$APP" --shell | \
        docker run -i -a stdin "$IMAGE" \
        /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/app-env.sh" | \
        commit_image "$IMAGE"
      pluginhook post-release "$APP"
    else
      # now do empty docker build to trigger ONBUILD hooks (manage or bootstrap jobs)
      echo "FROM $IMAGE" | docker build -t "$IMAGE" -

      docker create --env-file=<(: | pluginhook env-vars "$APP") "$IMAGE" | \
        commit_image "$IMAGE"
    fi
    tag_image "$IMAGE" "$IMAGE_GENERIC:release"
    ;;

  deploy)
    verify_app_name "$2"
    verify_max_args "3" "$@"

    if ! flock -n "$APP_BUILD_LOCK" true; then
      info2 "$APP is currently locked. We will wait for other process to finish."
    fi

    shift 1

    flock "$APP_BUILD_LOCK" dokku deploy:locked "$@"
    ;;

  deploy:locked)
    verify_app_name "$2"
    verify_max_args "3" "$@"

    TAG="${3:-release}"
    export IMAGE_TAG="$TAG"

    if ! docker_image_exists "$IMAGE_GENERIC:$IMAGE_TAG"; then
      info "Application ($IMAGE_GENERIC:$IMAGE_TAG) is not yet released!"
      exit 1
    fi

    # prepare for deployment
    info "Deploying $APP ..."
    pluginhook pre-deploy "$APP"

    if [[ ! -f "$DOKKU_ROOT/$APP/DISABLED" ]]; then
      DOCKER_ARGS="$(: | pluginhook docker-args "$APP" web)"

      # verify preboot
      PREBOOT="$(: | pluginhook use-preboot "$APP" "$IMAGE")"

      if [[ "$PREBOOT" == "" ]]; then
        stop_and_remove_app_containers
      fi

      # start the app
      if is_image_buildstep_based "$IMAGE_GENERIC:$TAG"; then
        START_CMD="/start"
      else
        START_CMD=""
      fi
      
      id=$(docker run --cidfile="$(get_cid_file_name app)" -d --name="$(get_app_container_name app)" $DOCKER_ARGS "$IMAGE_GENERIC:$TAG" $START_CMD)

      EXT_IP="$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' "$id")"
      EXT_PORT=""
      INT_PORT=""

      # enumare container ports and look to one from predefined list
      for CONTAINER_PORT in $(docker inspect --format '{{range $p, $conf := .NetworkSettings.Ports}}{{$p}} {{end}}' "$id"); do
        for port in $DOKKU_EXPOSED_PORTS; do
          if [[ "$port/tcp" == "$CONTAINER_PORT" ]]; then
            EXT_PORT="$port"
            INT_PORT="$port"
            break 2
          fi
        done
      done

      if [[ "$EXT_PORT" == "" ]] || [[ "$INT_PORT" == "" ]]; then
        echo -n > "$DOKKU_ROOT/$APP/URL"
        verbose "No external HTTP port published"
        pluginhook post-deploy "$APP" ""
        info2 "Application deployed"
      else
        if [[ "$DOKKU_SUPPRESS_FIREWALL_WARNING" != "1" ]] && [[ "$EXT_ADDR" == "0.0.0.0" ]]; then
          verbose ''
          verbose 'WARNING: You use potentially insecure docker configuration which exposes all ports to outside world.'
          verbose 'Due to the way Docker works most of Firewall scripts will not block access to docker exposed ports.'
          verbose 'Consider adding DOCKER_OPTS="$DOCKER_OPTS --ip=127.0.0.1" to /etc/default/docker and restart docker service.'
          verbose ''
        fi

        if [[ "$PREBOOT" != "" ]]; then
          verbose "Running pre-flight checks..."
          if ! pluginhook check-preboot "$APP" "$id" "$EXT_PORT" "$EXT_IP"
          then
            stop_and_remove_container "$id"
            fail "Application failed to deploy!"
          fi
        fi

        echo "http://$EXT_IP:$EXT_PORT" > "$DOKKU_ROOT/$APP/URL"
        pluginhook post-deploy "$APP" "$EXT_PORT" "$INT_PORT" "$EXT_IP"
        info2 "Application deployed:"
        verbose "$(dokku url $APP)"
      fi

      # save container ID
      echo "$id" > "$APP_NAME_FILE"

      if [[ "$TAG" != "deployed" ]]; then
        tag_image "$IMAGE_GENERIC:$TAG" "$IMAGE_GENERIC:deployed"
      fi

      if [[ "$PREBOOT" != "" ]]; then
        pluginhook post-preboot "$APP" "$id" || true
        stop_and_remove_app_containers "" "$id"
      fi
    else
      stop_and_remove_app_containers
      verbose "Application disabled - will not be run"
      pluginhook post-deploy "$APP" ""
      if [[ "$TAG" != "deployed" ]]; then
        tag_image "$IMAGE_GENERIC:$TAG" "$IMAGE_GENERIC:deployed"
      fi
    fi
    ;;

  cleanup)
    shift 1
    flock -n "$DOKKU_ROOT/cleanup.lock" dokku cleanup:locked "$@"
    ;;

  cleanup:locked)
    info "Cleaning up ..."
    docker ps -a | grep -v " Up " | grep -v "seconds ago" | grep -v "_data" | awk '{print $1}' | xargs docker rm &> /dev/null || true
    docker images --filter=dangling=true --quiet | xargs docker rmi &> /dev/null || true
    ;;

  plugins)
    ls -1 -d $PLUGIN_PATH/*/
    ;;

  plugins-install)
    [[ "$(id -un)" != "root" ]] && fail "Plugins install can be run only as root!"
    shift 1
    for script in $(ls -d $PLUGIN_PATH/*/install); do
      plugin="$(basename "$(dirname $script)")"
      echo "Installing $plugin..."
      $script "$@"
    done

    if [[ ! -e "$DOKKU_ROOT/.update.v1" ]]; then
      verbose ''
      verbose 'WARNING:'
      verbose 'Due to buildstep, security and docker changes all images needs to be rebuilt.'
      verbose 'It will take considerable amount of time to finish. Be patient and afterwards test your apps.'
      verbose ''
      verbose 'If something goes wrong you can force to rebuild specific app with: dokku rebuild:force app'
      verbose 'Or rebuild all apps: dokku rebuild:all:force'
      verbose ''
      touch "$DOKKU_ROOT/.update.v1"
      # due to buildstep, port and docker changes we need to rebuild all images
      dokku rebuild:all
    fi

    # we can proceed with normal deployment if we assume that rebuild were done already
    dokku deploy:all
    ;;

  plugins-install-dependencies)
    pluginhook dependencies
    ;;

  plugins-update)
    pluginhook update
    ;;

  # temporary hack for https://github.com/progrium/dokku/issues/82
  deploy:all)
    # redeploy lastly deployed image with tag :deployed
    for app in $(ls -d $DOKKU_ROOT/*/ 2>/dev/null); do
      if [[ -f "$app/refs/heads/master" ]]; then
        dokku deploy "$(basename $app)" "deployed" || true
      fi
    done
    wait
    ;;

  help|'')
    echo "Usage: dokku COMMAND <app> [command-specific-options]"
    echo ""

    cat<<EOF | pluginhook commands help | sort | sed 's/\s\s\s*/|/2' | column -t -s '|'
    help                                            Print the list of commands
    plugins                                         Print active plugins
    plugins-install                                 Install active plugins
    plugins-update                                  Update active plugins
EOF
    ;;

  *:help)
    CATEGORY="$(echo "$1" | cut -d':' -f1):"
    dokku help | grep "^\s*$CATEGORY"
    ;;

  *)
    implemented=0
    for script in $(ls -d $PLUGIN_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

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

esac
