#!/bin/bash

set -eu -o pipefail

if [[ ${EUID} -ne 0 ]]; then
    echo "This script should be run as root." > /dev/stderr
    exit 1
fi

if [[ $(lsb_release -rs) != "16.04" ]]; then
    echo "Cloudron requires Ubuntu 16.04" > /dev/stderr
    exit 1
fi

# change this to a hash when we make a upgrade release
readonly LOG_FILE="/var/log/cloudron-setup.log"
readonly DATA_FILE="/root/cloudron-install-data.json"
readonly MINIMUM_DISK_SIZE_GB="18" # this is the size of "/" and required to fit in docker images 18 is a safe bet for different reporting on 20GB min
readonly MINIMUM_MEMORY="974"      # this is mostly reported for 1GB main memory (DO 992, EC2 990, Linode 989, Serverdiscounter.com 974)

readonly curl="curl --fail --connect-timeout 20 --retry 10 --retry-delay 2 --max-time 2400"

# copied from cloudron-resize-fs.sh
readonly physical_memory=$(LC_ALL=C free -m | awk '/Mem:/ { print $2 }')
readonly disk_size_bytes=$(LC_ALL=C df --output=size / | tail -n1)
readonly disk_size_gb=$((${disk_size_bytes}/1024/1024))

# verify the system has minimum requirements met
if [[ "${physical_memory}" -lt "${MINIMUM_MEMORY}" ]]; then
    echo "Error: Cloudron requires atleast 1GB physical memory"
    exit 1
fi

if [[ "${disk_size_gb}" -lt "${MINIMUM_DISK_SIZE_GB}" ]]; then
    echo "Error: Cloudron requires atleast 20GB disk space (Disk space on / is ${disk_size_gb}GB)"
    exit 1
fi

initBaseImage="true"
# provisioning data
domain=""
provider=""
encryptionKey=""
restoreUrl=""
dnsProvider="manual"
tlsProvider="le-prod"
versionsUrl="https://s3.amazonaws.com/prod-cloudron-releases/versions.json"
requestedVersion="latest"
apiServerOrigin="https://api.cloudron.io"
dataJson=""
prerelease="false"
sourceTarballUrl=""
rebootServer="true"
baseDataDir=""

args=$(getopt -o "" -l "domain:,help,skip-baseimage-init,data:,data-dir:,provider:,encryption-key:,restore-url:,tls-provider:,version:,versions-url:,api-server:,dns-provider:,env:,prerelease,skip-reboot,source-url:" -n "$0" -- "$@")
eval set -- "${args}"

while true; do
    case "$1" in
    --domain) domain="$2"; shift 2;;
    --help) echo "See https://cloudron.io/references/selfhosting.html on how to install Cloudron"; exit 0;;
    --provider) provider="$2"; shift 2;;
    --encryption-key) encryptionKey="$2"; shift 2;;
    --restore-url) restoreUrl="$2"; shift 2;;
    --tls-provider) tlsProvider="$2"; shift 2;;
    --dns-provider) dnsProvider="$2"; shift 2;;
    --version) requestedVersion="$2"; shift 2;;
    --env)
        if [[ "$2" == "dev" ]]; then
            apiServerOrigin="https://api.dev.cloudron.io"
            versionsUrl="https://s3.amazonaws.com/dev-cloudron-releases/versions.json"
            tlsProvider="le-staging"
            prerelease="true"
        elif [[ "$2" == "staging" ]]; then
            apiServerOrigin="https://api.staging.cloudron.io"
            versionsUrl="https://s3.amazonaws.com/staging-cloudron-releases/versions.json"
            tlsProvider="le-staging"
            prerelease="true"
        fi
        shift 2;;
    --versions-url) versionsUrl="$2"; shift 2;;
    --api-server) apiServerOrigin="$2"; shift 2;;
    --skip-baseimage-init) initBaseImage="false"; shift;;
    --skip-reboot) rebootServer="false"; shift;;
    --data) dataJson="$2"; shift 2;;
    --prerelease) prerelease="true"; shift;;
    --source-url) sourceTarballUrl="$2"; version="0.0.1+custom"; shift 2;;
    --data-dir) baseDataDir=$(realpath "$2"); shift 2;;
    --) break;;
    *) echo "Unknown option $1"; exit 1;;
    esac
done

# validate arguments in the absence of data
if [[ -z "${dataJson}" ]]; then
    if [[ -z "${provider}" ]]; then
        echo "--provider is required (azure, digitalocean, ec2, lightsail, linode, ovh, rosehosting, scaleway, vultr or generic)"
        exit 1
    elif [[  \
                "${provider}" != "ami" && \
                "${provider}" != "azure" && \
                "${provider}" != "digitalocean" && \
                "${provider}" != "ec2" && \
                "${provider}" != "lightsail" && \
                "${provider}" != "linode" && \
                "${provider}" != "ovh" && \
                "${provider}" != "rosehosting" && \
                "${provider}" != "scaleway" && \
                "${provider}" != "vultr" && \
                "${provider}" != "generic" \
            ]]; then
        echo "--provider must be one of: azure, digitalocean, ec2, lightsail, linode, ovh, rosehosting, scaleway, vultr or generic"
        exit 1
    fi

    if [[ "${tlsProvider}" != "fallback" && "${tlsProvider}" != "le-prod" && "${tlsProvider}" != "le-staging" ]]; then
        echo "--tls-provider must be one of: le-prod, le-staging, fallback"
        exit 1
    fi

    if [[ -z "${dnsProvider}" ]]; then
        echo "--dns-provider is required (noop, manual)"
        exit 1
    elif [[ "${dnsProvider}" != "noop" && "${dnsProvider}" != "manual" ]]; then
        echo "--dns-provider must be one of : manual, noop"
        exit 1
    fi

    if [[ -n "${baseDataDir}" && ! -d "${baseDataDir}" ]]; then
        echo "${baseDataDir} does not exist"
        exit 1
    fi
fi

echo ""
echo "##############################################"
echo "        Cloudron Setup (${requestedVersion})  "
echo "##############################################"
echo ""
echo " Follow setup logs in a second terminal with:"
echo " $ tail -f ${LOG_FILE}"
echo ""
echo " Join us at https://chat.cloudron.io for any questions."
echo ""

if [[ "${initBaseImage}" == "true" ]]; then
    echo "=> Updating apt and installing script dependencies"
    if ! apt-get update &>> "${LOG_FILE}"; then
        echo "Could not update package repositories"
        exit 1
    fi

    if ! apt-get install curl python3 ubuntu-standard -y &>> "${LOG_FILE}"; then
        echo "Could not install setup dependencies (curl)"
        exit 1
    fi
fi

echo "=> Checking version"
if [[ "${sourceTarballUrl}" == "" ]]; then
    releaseJson=$($curl -s "${versionsUrl}")
    if [[ "$requestedVersion" == "latest" ]]; then
        pre=$([[ "${prerelease}" == "true" ]] && echo "null" || echo "-pre")
        version=$(echo "${releaseJson}" | python3 -c "import json,sys,collections;obj=json.load(sys.stdin, object_pairs_hook=collections.OrderedDict);latest=list(v for v in obj if '${pre}' not in v)[-1];print(latest)")
    else
        version="${requestedVersion}"
    fi
    if ! sourceTarballUrl=$(echo "${releaseJson}" | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj[sys.argv[1]]["sourceTarballUrl"])' "${version}"); then
        echo "No source code for version ${requestedVersion}"
        exit 1
    fi
fi

# Build data
if [[ -z "${dataJson}" ]]; then
    if [[ -z "${restoreUrl}" ]]; then
        data=$(cat <<EOF
    {
        "boxVersionsUrl": "${versionsUrl}",
        "fqdn": "${domain}",
        "provider": "${provider}",
        "apiServerOrigin": "${apiServerOrigin}",
        "tlsConfig": {
            "provider": "${tlsProvider}"
        },
        "dnsConfig": {
            "provider": "${dnsProvider}"
        },
        "backupConfig" : {
            "provider": "filesystem",
            "backupFolder": "/var/backups",
            "key": "${encryptionKey}"
        },
        "updateConfig": {
            "prerelease": ${prerelease}
        },
        "version": "${version}"
    }
EOF
    )
    else
        data=$(cat <<EOF
    {
        "boxVersionsUrl": "${versionsUrl}",
        "fqdn": "${domain}",
        "provider": "${provider}",
        "apiServerOrigin": "${apiServerOrigin}",
        "restore": {
            "url": "${restoreUrl}",
            "key": "${encryptionKey}"
        },
        "version": "${version}"
    }
EOF
    )
    fi
else
    data="${dataJson}"
fi

echo "=> Downloading version ${version} ..."
box_src_tmp_dir=$(mktemp -dt box-src-XXXXXX)

if ! $curl -sL "${sourceTarballUrl}" | tar -zxf - -C "${box_src_tmp_dir}"; then
    echo "Could not download source tarball. See ${LOG_FILE} for details"
    exit 1
fi

if [[ "${initBaseImage}" == "true" ]]; then
    echo -n "=> Installing base dependencies and downloading docker images (this takes some time) ..."
    if ! /bin/bash "${box_src_tmp_dir}/baseimage/initializeBaseUbuntuImage.sh" "${provider}" "../src" &>> "${LOG_FILE}"; then
        echo "Init script failed. See ${LOG_FILE} for details"
        exit 1
    fi
    echo ""
fi

echo "=> Installing version ${version} (this takes some time) ..."
echo "${data}" > "${DATA_FILE}"
if ! /bin/bash "${box_src_tmp_dir}/scripts/installer.sh" --data-file "${DATA_FILE}" --data-dir "${baseDataDir}" &>> "${LOG_FILE}"; then
    echo "Failed to install cloudron. See ${LOG_FILE} for details"
    exit 1
fi
rm "${DATA_FILE}"

echo -n "=> Waiting for cloudron to be ready (this takes some time) ..."
while true; do
    echo -n "."
    if status=$($curl -q -f "http://localhost:3000/api/v1/cloudron/status" 2>/dev/null); then
        [[ -z "$domain" ]] && break # with no domain, we are up and running
        [[ "$status" == *"\"tls\": true"* ]] && break # with a domain, wait for the cert
    fi
    sleep 10
done

if [[ -n "${domain}" ]]; then
    echo -e "\n\nVisit https://my.${domain} to finish setup once the server has rebooted.\n"
else
    echo -e "\n\nVisit https://<IP> to finish setup once the server has rebooted.\n"
fi

if [[ "${rebootServer}" == "true" ]]; then
    echo -e "\n\nRebooting this server now to let bootloader changes take effect.\n"
    systemctl reboot
fi
