#!/bin/bash
[[ $DOKKU_TRACE ]] && set -x

if [[ -z "$3"  ]]; then
  APP=$2
  DATABASE=$2
else
  APP=$2
  DATABASE=$3
fi

OLDHOME=$HOME
HOME="$DOKKU_ROOT/.mongodb"

fail() {
	echo "$@" 1>&2
	exit 1
}

verify_min_args() {
	local MIN_ARGS="$1"
	[[ $# -le $((MIN_ARGS+1)) ]] && fail "Invalid number of arguments ($(($#-2)), expected $MIN_ARGS)."
}

check_exists() {
  if [[ ! -d "$DOKKU_ROOT/$APP" ]]; then
    fail "App must exist before creating mongodb storage for it"
  fi
}

check_app() {
  if [[ -z "$APP" ]]; then
    fail "You must specify an app name"
  fi
}

# checks whether a database exists
check_db() {
  db=$(mongo $mongodb_public_ip:$mongodb_port --quiet -u admin -p"$admin_pass" --authenticationDatabase="admin" --eval "printjson(db.adminCommand('listDatabases'));" | grep name | cut -d \" -f 4 |grep -x $1)

  if [[ -z "$db" ]]; then
    fail "The database $1 doesn't exist"
  fi
}

check_container() {
  if [[ -z "$id" ]]; then
    fail "MongoDB container not started cannot continue. Start with dokku mongodb:start"
  fi
}

set_dokku_env() {
  env_var=$1
  env_val=$2
  ex -s -c "g/export ${env_var}=/d" -c "x" "$DOKKU_ROOT/$APP/ENV"
  echo "export ${env_var}=\"${env_val}\"" >> "$DOKKU_ROOT/$APP/ENV"
}

MONGODB_VERSION=""
get_mongodb_version() {
  MONGODB_VERSION=$(docker run --rm jeffutter/mongodb mongod --version | egrep -o '[[:digit:]\.]*'|head -n1)
}

MONGODB_AUTH_SCHEMA_VERSION=""
get_mongodb_auth_schema_version() {
  MONGODB_AUTH_SCHEMA_VERSION=$(mongo $mongodb_public_ip:$mongodb_port --quiet -u admin -p"$admin_pass" --authenticationDatabase="admin" --eval "db.adminCommand({getParameter: 1, authSchemaVersion: 1})['authSchemaVersion'];")
}

vercomp () {
  if [[ $1 == $2 ]]
  then
    return 0
  fi
  local IFS=.
  local i ver1=($1) ver2=($2)
  # fill empty fields in ver1 with zeros
  for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
  do
    ver1[i]=0
  done
  for ((i=0; i<${#ver1[@]}; i++))
  do
    if [[ -z ${ver2[i]} ]]
    then
      # fill empty fields in ver2 with zeros
      ver2[i]=0
    fi
    if ((10#${ver1[i]} > 10#${ver2[i]}))
    then
      return 1
    fi
    if ((10#${ver1[i]} < 10#${ver2[i]}))
    then
      return 2
    fi
  done
  return 0
}

get_mongodb_version
vercomp "$MONGODB_VERSION" '2.5.9999999999999999'                                                                                                                                                   
if [[ $? == 1 ]]; then
  # 2.6
  db_image="jeffutter/mongodb:latest"
else
  # 2.4
  db_image="jeffutter/mongodb:2.4"
fi

admin_pass=$(cat "$DOKKU_ROOT/.mongodb/admin_pw")
mongodb_database="${DATABASE//./_}-production"

id=$(docker ps | grep "$db_image" |  awk '{print $1}')
if [[ -n "$id" ]]; then
  mongodb_public_ip=$(docker port ${id} 27017 | awk '{split($0,a,":"); print a[1]}')
  mongodb_private_ip=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${id})
  mongodb_port=$(docker port ${id} 27017 | awk '{split($0,a,":"); print a[2]}')
  if [[ $mongodb_public_ip = "0.0.0.0" ]]; then
    mongodb_public_ip=localhost
  fi
fi

case "$1" in
  mongodb:console)
    check_container
    mongo $mongodb_public_ip:$mongodb_port -u admin -p"$admin_pass" --authenticationDatabase="admin"
    ;;
  mongodb:create)
    check_container
    check_app

    mongodb_password=$(openssl rand -base64 32|base64)

    if [[ -z "$3"  ]]; then
      echo $mongodb_password > "$DOKKU_ROOT/.mongodb/pass_${APP}"
    else
      echo $mongodb_password > "$DOKKU_ROOT/.mongodb/pass_${APP}_${DATABASE}"
    fi
    

    mongodb_username=$APP
    
    get_mongodb_auth_schema_version
    vercomp "$MONGODB_AUTH_SCHEMA_VERSION" '2.9999999'
    if [[ $? == 1 ]]; then
      mongo $mongodb_public_ip:$mongodb_port/$mongodb_database --quiet -u admin -p"$admin_pass" --authenticationDatabase="admin" --eval "db.createUser({user: \"${mongodb_username}\", pwd: \"${mongodb_password}\", roles : [{ role : \"dbOwner\", db: \"${mongodb_database}\"}] })"
    else
      mongo $mongodb_public_ip:$mongodb_port/$mongodb_database --quiet -u admin -p"$admin_pass" --authenticationDatabase="admin" --eval "db.addUser(\"${mongodb_username}\", \"${mongodb_password}\")"
    fi
    ;;
  mongodb:delete)
    check_container
    check_app

    mongo $mongodb_public_ip:$mongodb_port/$mongodb_database --quiet -u admin -p"$admin_pass" --authenticationDatabase="admin" --eval "printjson(db.dropDatabase())"

    dokku config:unset "$APP" MONGODB_DATABASE \
      MONGODB_HOST \
      MONGODB_USERNAME \
      MONGODB_PASSWORD \
      MONGODB_PORT \
      MONGO_URL \
      MONGO_URI
    ;;
  mongodb:dump)
    verify_min_args 1 "$@"
    check_container
    check_db "$2"

    # set backup root dir
    if [[ -d "$4" ]]; then
      dumproot=$4
    else
      dumproot="$PWD"
    fi

    # echo "dump will save to $dumproot"
    # echo "dump run by $(whoami) user"
  
    logtime=$(date +%Y-%m-%d-%Hh%Mm%Ss)
    dumpdir="$dumproot/$2-$logtime"

    dumpfile="$dumpdir"
    mongodump -h $mongodb_public_ip:$mongodb_port -u admin -p"$admin_pass" -d"$2" --authenticationDatabase="admin" --out "$dumpdir" > /dev/null
    if [ $? -eq 0 ]; then
      # tar.gz dump
      if [[ $3 == "-tar" ]]; then 
        dumpfile="$dumpdir.tar.gz"
        tar -zcP -C "$dumpdir" -f "$dumpfile" "$2"
        rm -rf "$dumpdir"
      fi
      echo "Saved dump successfully to $dumpfile"
    # an error occured during the dump
    else 
      fail "An error occured while executing mongodump.
Please check whether you have sufficient write privileges in $PWD"
    fi
    ;;
  mongodb:link)
    if [[ -d "$DOKKU_ROOT/$APP" ]]; then
      mongodb_internal_port=27017
      if [[ -z "$3"  ]]; then
        mongodb_password=$(cat "$DOKKU_ROOT/.mongodb/pass_${APP}")
      else
        mongodb_password=$(cat "$DOKKU_ROOT/.mongodb/pass_${APP}_${DATABASE}")
      fi

      mongodb_username=$APP

      dokku config:set-norestart "$APP" MONGODB_DATABASE="$mongodb_database" MONGODB_HOST="$mongodb_private_ip" MONGODB_PORT="$mongodb_internal_port" MONGODB_USERNAME="$mongodb_username" MONGODB_PASSWORD="$mongodb_password" MONGO_URL="mongodb://${mongodb_username}:${mongodb_password}@${mongodb_private_ip}:${mongodb_internal_port}/${mongodb_database}" MONGO_URI="mongodb://${mongodb_username}:${mongodb_password}@${mongodb_private_ip}:${mongodb_internal_port}/${mongodb_database}"

      echo
      echo "-----> $APP linked to $db_image container"
    fi
    ;;
  mongodb:list)
    check_container
    mongo $mongodb_public_ip:$mongodb_port --quiet -u admin -p"$admin_pass" --authenticationDatabase="admin" --eval "printjson(db.adminCommand('listDatabases'));" | grep name | cut -d \" -f 4 |grep -v admin | grep -v test
    ;;
  mongodb:logs)
    check_container
    docker logs dokku-mongodb | tail -n 100
    ;;
  mongodb:restore) 
    verify_min_args 2 "$@"
    check_container

    fileOrDir="${3%/}"
    # does file or folder exist?
    if [[ ! -d "$fileOrDir" && ! -f "$fileOrDir" ]]; then
      fail "$fileOrDir doesn't exist"
    fi
    
    if [[ $fileOrDir == *"tar.gz" ]]; then 
      randomstring=$(cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)
      tempdir="/tmp/mongodump/$randomstring"
      mkdir -p "$tempdir"
      tar -xzvf $fileOrDir -C "$tempdir" > /dev/null
      fileOrDir="$tempdir/$2"
      if [[ ! -d "$fileOrDir" ]]; then
        fail "The .tar.gz-file didn't contain a database-dump for $2"
      fi
    fi
    output=$(mongorestore -h $mongodb_public_ip:$mongodb_port -u admin -p"$admin_pass" -d"$2" --authenticationDatabase="admin" --drop "$fileOrDir")  
    if [ $? -eq 0 ]; then
      echo "Successfully restored database $2 from $3"
    else
      fail "An error occured while executing mongorestore
This is the output of the mongorestore-command:
$output"
    fi
    # cleanup temp-dir
    if [ ! -z ${tempdir+x} ]; then
      rm -rf "$tempdir"
    fi
    ;;
  mongodb:start)
    if [[ "$id" != "" ]]; then
      echo "MongoDB container already running with ID: ${id}"
    else
      stopped_id=$(docker ps -a | grep "$db_image[[:space:]].\+[[:space:]]dokku-mongodb[[:space:]]*$" |  awk '{print $1}')
      if [[ "$stopped_id" != "" ]]; then
        docker start $stopped_id
      else
        docker run -p 27017 -v "$DOKKU_ROOT/.mongodb/data":/tmp/mongo --name="dokku-mongodb" -d "$db_image" /usr/bin/mongod --dbpath=/tmp/mongo --auth
      fi
    fi
    ;;
  mongodb:stop)
    check_container
    mongo $mongodb_public_ip:$mongodb_port/admin --quiet -u admin -p"$admin_pass" --authenticationDatabase="admin" --eval "db.shutdownServer()"
    ;;
  mongodb:status)
    if [[ "$id" != "" ]]; then
      echo "MongoDB container running with ID: ${id}"
    else
      echo "MongoDB container not running"
    fi
    ;;
  help)
    cat && cat<<EOF
    mongodb:console                 		    Launch an admin mongodb console
    mongodb:create <app> <database> 		    Create a Mongo database and optional params for app
    mongodb:delete <app> <database> 		    Delete specified Mongo database
    mongodb:dump <database> [-tar]	   	    Creates a binary export of the contents of a database (-tar tarball dump)
    mongodb:link <app> <database> 	            Set ENV variables for app if database exists
    mongodb:list                    		    List all databases
    mongodb:logs                    		    Show logs from MongoDB program
    mongodb:restore <database> <file-or dirname>    Restores the state of a database exported with mongodb:dump
    mongodb:start                   		    Start the MongoDB docker container if it isn't running
    mongodb:stop                    		    Stop the MongoDB docker container
    mongodb:status                  		    Shows status of MongoDB
EOF
    ;;
esac

HOME=$OLDHOME
