#!/usr/bin/env bash

set -o nounset
set -o errexit
set -o pipefail

# FUNCTIONS 
function ask_questions {
  
  while [[ -z "$API_HOST" ]] && [[ "$API_HOST" != *[.]*[.]* ]]
  do
  echo -ne "Enter the subdomain for the backend (e.g. api.example.com): "
  read API_HOST
  done
  echo "API_HOST is set to ${API_HOST}"

  while [[ -z "$APP_HOST" ]] && [[ "$APP_HOST" != *[.]*[.]* ]]
  do
  echo -ne "Enter the subdomain for the frontend (e.g. rmm.example.com): "
  read APP_HOST
  done
  echo "APP_HOST is set to ${APP_HOST}"

  while [[ -z "$MESH_HOST" ]] && [[ "$MESH_HOST" != *[.]*[.]* ]]
  do
  echo -ne "Enter the subdomain for meshcentral (e.g. mesh.example.com): "
  read MESH_HOST
  done
  echo "MESH_HOST is set to ${MESH_HOST}"

  while [[ -z "$EMAIL" ]] && [[ "$EMAIL" != *[@]*[.]* ]]
  do
  echo -ne "Enter a valid email address for django and meshcentral: "
  read EMAIL
  done
  echo "EMAIL is set to ${EMAIL}"

  while [[ -z "$USERNAME" ]]
  do
  echo -ne "Set username for mesh and tactical login: "
  read USERNAME
  done
  echo "USERNAME is set to ${USERNAME}"

  while [[ -z "$PASSWORD" ]]
  do
  echo -ne "Set password for mesh and tactical password: "
  read PASSWORD
  done
  echo "PASSWORD is set"

  # check if let's encrypt or cert-keys options were set
  if [[ -z "$LETS_ENCRYPT" ]] && [[ -z "$CERT_PRIV_FILE" ]] || [[ -z "$CERT_PUB_FILE" ]]; then
    echo -ne "Create a let's encrypt certificate?[Y,n]: "
    read USE_LETS_ENCRYPT

    [[ "$USE_LETS_ENCRYPT" == "" ]] || [[ "$USE_LETS_ENCRYPT" ~= [Yy] ]] && LETS_ENCRYPT=1

    if [[ -z "$LET_ENCRYPT" ]]; then
      echo "Let's Encrypt will not be used"

      echo -ne "Do you want to specify paths to a certificate public key and private key?[Y,n]: "
      read PRIVATE_CERTS

      if [[ "$PRIVATE_CERTS" == "" ]] || [[ "$PRIVATE_CERTS" ~= [yY] ]]; then
        
        # check for valid public certificate file
        while [[ ! -f $CERT_PUB_FILE ]]
        do
          echo -ne "Enter a valid full path to public key file: "
          read CERT_PUB_FILE
        done

        # check for valid private key file
        while [[ ! -f $CERT_PRIV_FILE ]]
        do
          echo -ne "Enter a valid full path to private key file: "
          read CERT_PRIV_FILE
        done
      fi
    fi
  fi

}

function encode_certificates {
  echo "Base64 encoding certificates"
  CERT_PUB_BASE64="$(sudo base64 -w 0 ${CERT_PUB_FILE})"
  CERT_PRIV_BASE64="$(sudo base64 -w 0 ${CERT_PRIV_FILE})"
}

function generate_env {
  [[ -f "$ENV_FILE" ]] && echo "Env file already exists"; return 0;

  local mongodb_user=$(cat /dev/urandom | tr -dc 'a-z' | fold -w 8 | head -n 1)
  local mongodb_pass=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1)
  local postgres_user=$(cat /dev/urandom | tr -dc 'a-z' | fold -w 8 | head -n 1)
  local postgres_pass=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1)

  echo "Generating env file in ${INSTALL_DIR}"
  local config_file="$(cat << EOF
IMAGE_REPO=${DOCKER_REPO}
VERSION=${VERSION}
TRMM_USER=${USERNAME}
TRMM_PASS=${PASSWORD}
APP_HOST=${APP_HOST}
API_HOST=${API_HOST}
MESH_HOST=${MESH_HOST}
MESH_USER=${USERNAME}
MESH_PASS=${PASSWORD}
MONGODB_USER=${mongogb_user}
MONGODB_PASSWORD=${mongodb_pass}
POSTGRES_USER=${postgres_user}
POSTGRES_PASS=${postgres_pass}
EOF
)"
  echo "${env_file}" > "$ENV_FILE"
}

function update_env_field {

  
}

function get_env_field {
  local search_field="$1"
  awk -F "=" '{if ($1==$search_field) { print $2" } }' $ENV_FILE
}

function initiate_letsencrypt {
  echo "Starting Let's Encrypt"

  ROOT_DOMAIN=$(echo ${API_HOST} | cut -d "." -f2- )

  echo "Root domain is ${ROOTDOMAIN}"
  sudo certbot certonly --manual -d *.${ROOT_DOMAIN} --agree-tos --no-bootstrap --preferred-challenges dns -m ${EMAIL} --no-eff-email
  while [[ $? -ne 0 ]]
  do
  sudo certbot certonly --manual -d *.${ROOT_DOMAIN} --agree-tos --no-bootstrap --preferred-challenges dns -m ${EMAIL} --no-eff-email
  done

  CERT_PRIV_FILE=/etc/letsencrypt/live/${ROOT_DOMAIN}/privkey.pem
  CERT_PUB_FILE=/etc/letsencrypt/live/${ROOT_DOMAIN}/fullchain.pem
}


# setup defaults
# keep track of first arg
FIRST_ARG="$1"

# defaults
DOCKER_REPO="tacticalrmm/"
REPO="amidaware"
BRANCH="master"
VERSION="latest"

# file locations
INSTALL_DIR=/opt/tactical
ENV_FILE=/opt/tactical/.env

# check prerequisites
command -v docker >/dev/null 2>&1 || { echo >&2 "Docker must be installed. Exiting..."; exit 1; }
command -v docker-compose >/dev/null 2>&1 || { echo >&2 "Docker Compose must be installed. Exiting..."; exit 1; }
command -v curl >/dev/null 2>&1 || { echo >&2 "Curl must be installed. Exiting..."; exit 1; }
command -v bash >/dev/null 2>&1 || { echo >&2 "Bash must be installed. Exiting..."; exit 1; }

# check for arguments
[ -z "$1" ] && echo >&2 "No arguments supplied. Exiting..."; exit 1;

# parse arguments
while [[ $# -gt 0 ]]
do
key="$1"

  case $key in
    # install arg
    -i|install)
    [[ "$key" != "$FIRST_ARG" ]] && echo >&2 "install must be the first argument. Exiting.."; exit 1;
    MODE="install"
    shift # past argument
    ;;

    # update arg
    -u|update)
    [[ "$key" != "$FIRST_ARG" ]] && echo >&2 "update must be the first argument. Exiting..."; exit 1;
    MODE="update"
    shift # past argument
    ;;

    # backup arg
    -b|backup)
    [[ "$key" != "$FIRST_ARG" ]] && echo >&2 "backup must be the first argument. Exiting..."; exit 1;
    MODE="backup"
    shift # past argument
    ;;

    # restore arg
    -r|restore)
    [[ "$key" != "$FIRST_ARG" ]] && echo >&2 "restore must be the first argument. Exiting..."; exit 1;
    MODE="restore"
    shift # past argument
    ;;

    # update-cert arg
    -c|update-cert)
    [[ "$key" != "$FIRST_ARG" ]] && echo >&2 "update-cert must be the first argument. Exiting..."; exit 1;
    MODE="update-cert"
    shift # past argument
    ;;

    # use-lets-encrypt arg
    --use-lets-encrypt)
    [[ -z "$MODE" ]] && echo >&2 "Missing install or update-cert as first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] || [[ "$MODE" != "update-cert" ]] && \
      echo >&2 "--use-lets-encrypt option only valid for install and update-cert. Exiting..."; exit 1;
    LETS_ENCRYPT=1
    shift # past argument
    ;;

    # cert-priv-file arg
    --cert-priv-file)
    [[ -z "$MODE" ]] && echo >&2 "Missing install or update-cert first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] || [[ "$MODE" != "update-cert" ]] && \
      echo >&2 "--cert-priv-file option only valid for install and update-cert. Exiting..."; exit 1;

    shift # past argument
    [ ! -f "$key" ] && echo >&2 "Certificate private key file $key does not exist. Use absolute paths. Exiting..."; exit 1;
    CERT_PRIV_FILE="$key"
    shift # past value
    ;;

    # cert-pub-file arg
    --cert-pub-file)
    [[ -z "$MODE" ]] && echo >&2 "Missing install or update-cert first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] || [[ "$MODE" != "update-cert" ]] && \
      echo >&2 "--cert-pub-file option only valid for install and update-cert. Exiting..."; exit 1;

    shift # past argument
    [ ! -f "$key" ] && echo >&2 "Certificate public Key file ${key} does not exist. Use absolute paths. Exiting..."; exit 1;
    CERT_PUB_FILE="$key"
    shift # past value
    ;;

    # local arg
    --local)
    [[ -z "$MODE" ]] && echo >&2 "Missing install or update first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] || [[ "$MODE" != "update" ]] && \
      echo >&2 "--local option only valid for install and update. Exiting..."; exit 1;
    DOCKER_REPO=""
    shift # past argument
    ;;

    # repo arg
    --repo)
    [[ -z "$MODE" ]] && echo >&2 "Missing install or update first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] || [[ "$MODE" != "update" ]] && \
      echo >&2 "--repo option only valid for install and update. Exiting..."; exit 1;

    shift # past argument
    REPO="$key"
    shift # past value
    ;;

    # branch arg
    --branch)
    [[ -z "$MODE" ]] && echo >&2 "Missing install or update first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] || [[ "$MODE" != "update" ]] && \
      echo >&2 "--branch option only valid for install and update. Exiting..."; exit 1;

    shift # past argument
    BRANCH="$key"
    shift # past value
    ;;

    # version arg
    --version)
    [[ -z "$MODE" ]] && echo >&2 "Missing install or update first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] || [[ "$MODE" != "update" ]] && \
      echo ">&2 --version option only valid for install and update. Exiting..."; exit 1;

    shift # past argument
    VERSION="$key"
    shift # past value
    ;;

    # noninteractive arg
    --noninteractive)
    [[ -z "$MODE" ]] && echo >&2 "Missing install first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] && echo >&2 "--noninteractive option only valid for install. Exiting..."; exit 1;
    NONINTERACTIVE=1

    shift # past argument
    ;;

    # app host arg
    --app-host)
    [[ -z "$MODE" ]] && echo >&2 "Missing install first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] && echo >&2 "--app-host option only valid for install. Exiting..."; exit 1;

    shift # past argument
    APP_HOST="$key"
    shift # past value
    ;;

    # api host arg
    --api-host)
    [[ -z "$MODE" ]] && echo >&2 "Missing install first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] && echo >&2 "--api-host option only valid for install. Exiting..."; exit 1;

    shift # past argument
    API_HOST="$key"
    shift # past value
    ;;

    # mesh host arg
    --mesh-host)
    [[ -z "$MODE" ]] && echo >&2 "Missing install first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] && echo >&2 "--mesh-host option only valid for install. Exiting..."; exit 1;

    shift # past argument
    MESH_HOST="$key"
    shift # past value
    ;;

    # tactical user arg
    --tactical-user)
    [[ -z "$MODE" ]] && echo >&2 "Missing install first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] && echo >&2 "--tactical-user option only valid for install. Exiting..."; exit 1;

    shift # past argument
    USERNAME="$key"
    shift # past value
    ;;

    # tactical password arg
    --tactical-password)
    [[ -z "$MODE" ]] && echo >&2 "Missing install first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] && echo >&2 "--tactical-password option only valid for install. Exiting..."; exit 1;

    shift # past argument
    PASSWORD="$key"
    shift # past value
    ;;

    # email arg
    --email)
    [[ -z "$MODE" ]] && echo >&2 "Missing install first argument. Exiting..."; exit 1; 
    [[ "$MODE" != "install" ]] && echo >&2 "--email option only valid for install. Exiting..."; exit 1;

    shift # past argument
    EMAIL="$key"
    shift # past value
    ;;

    # Unknown arg
    *)
    echo "Unknown argument ${$1}. Exiting..."
    exit 1
    ;;
  esac
done


# for install mode
if [[ "$MODE" == "install" ]]; then
  echo "Starting installation in ${INSTALL_DIR}"

  # move to install dir
  mkdir -p "${INSTALL_DIR}"
  cd "$INSTALL_DIR"

  # pull docker-compose.yml file
  echo "Downloading docker-compose.yml from branch ${BRANCH}"
  COMPOSE_FILE="https://raw.githubusercontent.com/${REPO}/tacticalrmm/${BRANCH}/docker/docker-compose.yml"
  if ! curl -sS "${COMPOSE_FILE}"; then
    echo >&2 "Failed to download installation package ${COMPOSE_FILE}"
    exit 1
  fi
  
  # check if install is noninteractive
  if [[ -z "$NONINTERACTIVE" ]]; then
    # ask user for information not supplied as arguments
    ask_questions

  else
    echo "NonInteractive mode set."
    # check for required noninteractive arguments
    [[ -z "$API_HOST" ]] || \
    [[ -z "$APP_HOST" ]] || \
    [[ -z "$MESH_HOST" ]] || \
    [[ -z "$EMAIL" ]] || \
    [[ -z "$USERNAME" ]] || \
    [[ -z "$PASSWORD" ]] && \
    echo "You must supply additional arguments for noninteractive install."; exit 1;
  fi
  
  # if certificates are available base64 encode them
  if [[ -n "$LET_ENCRYPT" ]] && [[ -z "$NONINTERACTIVE" ]]; then
    initiate_letsencrypt
    encode_certificates
  elif [[ -n "$CERT_PUB_FILE" ]] && [[ -n "$CERT_PRIV_FILE" ]]; then
    encode_certificates

  # generate config file
  generate_config

  # generate env file
  generate_env

  echo "Configuration complete. Starting environment."
  # start environment
  docker-compose pull
  docker-compose up -d

fi

# for update mode
if [[ "$MODE" == "update" ]]; then
  [[ "$VERSION" != "latest" ]]
  docker-compose pull
  docker-compose up -d
fi

# for update cert mode
if [[ "$MODE" == "update-cert" ]]; then
  # check for required parameters
  [[ -z "$LET_ENCRYPT" ]] || \
  [[ -z "$CERT_PUB_FILE" ]] && \
  [[ -z "$CERT_PRIV_FILE" ]] && \
    echo >&2 "Provide the --lets-encrypt option or use --cert-pub-file and --cert-priv-file. Exiting..."; exit;

  if [[ -n "$LET_ENCRYPT" ]]; then
    initiate_letsencrypt
    encode_certificates
    generate_env
  elif [[ -n "$CERT_PUB_FILE" ]] && [[ -n "$CERT_PRIV_FILE" ]]; then
    encode_certificates
    generate_env

  docker-compose restart
fi

# for backup mode
if [[ "$MODE" == "backup" ]]; then
  echo "backup not yet implemented"
fi

# for restore mode
if [[ "$MODE" == "restore" ]] then;
  echo "restore not yet implemented"
fi
