#!/usr/bin/env bash

_banner() {
    echo -e "\e[92m"
cat <<EOL
 =================================================

   |  |--.---.-.-----.|  |_.----.-----.-----.
   |    <|  _  |     ||   _|   _|  -__|  -__|
   |__|__|___._|__|__||____|__| |_____|_____|

                    Kantree

 =================================================

EOL
    echo -e "\e[39m"
}

# ---------------------------------------------------------------

prev_path=$(pwd)
cd $(dirname $0)

CMD=help
if [ -n "$1" ]; then
    CMD=$1; shift
fi

CONFIG_FILE=${KANTREE_CONFIG_FILE:-config.yml}
FORCE_RPM=$KANTREE_FORCE_RPM
UNATTENDED=$KANTREE_UNATTENDED
PYTHON_BIN=${KANTREE_PYTHON_BIN:-python3.10}
export POETRY_VENV_PATH=${KANTREE_POETRY_VENV_PATH:-./.poetry-venv}
if [ -n "$POETRY_VENV_PATH" ]; then
    export POETRY_BIN=$POETRY_VENV_PATH/bin/poetry
else
    export POETRY_BIN=poetry
fi

# ---------------------------------------------------------------

_echostep() {
    echo -e "\e[92m => $@\e[39m"
}

_echoerr() {
    echo -e "\n\e[91m /!\ ERROR: $@\e[39m\n" 1>&2;
}

_echotip() {
    echo -e "\n\e[34m (?) TIP: $@\e[39m\n" 1>&2;
}

_checkcmd() {
    if [ $1 -ne 0 ]; then
        if [ -n "$2" ]; then
            _echoerr "$2"
        fi
        deactivate 2>/dev/null #make sure we deactivate venv
        exit 1
    fi
}

_needvenv() {
    if [ ! -e $POETRY_BIN ] || [ -z $($POETRY_BIN env info -p) ]; then
        _echoerr "Missing virtualenv, please run 'init' first"
        exit 1
    fi
}

_ask() {
    if [ -n "$UNATTENDED" ]; then
        ASK_VALUE=$2
    else
        read -p "$1" ASK_VALUE
        if [ -z "$ASK_VALUE" ]; then
            ASK_VALUE=$2
        fi
    fi
}

# ---------------------------------------------------------------

check-system() {
    if ! which dpkg &>/dev/null; then
        _echoerr "This is an unsupported system"
        _ask "Continue install anyway (y,N)? " n
        case $ASK_VALUE in
            [Yy]*) return;;
            *) exit 1;;
        esac
    fi

    packages="git build-essential software-properties-common libxml2-dev libxslt1-dev libjpeg-dev pkg-config \
zlib1g-dev unzip libssl-dev libjson-c-dev libcurl4-openssl-dev libpq-dev libldap2-dev \
libsasl2-dev libffi-dev libxmlsec1-dev libxmlsec1-openssl"

    _echostep "Checking system packages"
    missing=
    for pkg in $packages
    do
        dpkg -s $pkg &> /dev/null
        if [ $? -ne 0 ]; then
            missing="$missing $pkg"
        fi
    done

    if [ -n "$missing" ]; then
        _echoerr "Some needed system packages are missing:$missing"
        _ask "Install now (Y,n)? " y
        case $ASK_VALUE in
            [Nn]*)
                _ask "Continue install anyway (y,N)? " n
                case $ASK_VALUE in
                    [Yy]*);;
                    *) exit 1;;
                esac
            ;;
            * )
                apt-get install -y $missing
                _checkcmd $? "Some packages failed to install."
            ;;
        esac
    fi

    _echostep "Checking Python version"
    pyver=$($PYTHON_BIN -V 2>&1 | cut -d' ' -f2 | cut -d'.' -f1,2)
    if [ "$pyver" != "3.10" ]; then
        _echoerr "Python 3.10 is required"
        _ask "Install now using deadsnakes PPA (Y,n)? " y
        case $ASK_VALUE in
            [Nn]*)
                _ask "Continue install anyway (y,N)? " n
                case $ASK_VALUE in
                    [Yy]*);;
                    *) exit 1;;
                esac
            ;;
            * )
                add-apt-repository -y ppa:deadsnakes/ppa &&\
                apt-get update &&\
                apt-get install -y python3.10 python3.10-venv python3.10-dev
                _checkcmd $? "Failed to install Python 3.10"
            ;;
        esac
    else
        apt-get install -y python3-venv python3-dev
    fi
}

check_system() { # back compat
    check-system "$@"
}

createdb() {
    _echostep "Creating 'kantree' database in Postgresql"
    _ask "Password for Kantree's database user (leave empty to auto generate one): "; password="$ASK_VALUE"
    if [ -z "$password" ]; then
        password=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
        echo "Generated password: $password"
    fi
    (
        cd /tmp && \
        psql "$@" -c "CREATE DATABASE kantree ENCODING 'UTF-8';" && \
        psql "$@" -c "CREATE USER kantree WITH PASSWORD '$password'; GRANT ALL PRIVILEGES ON DATABASE kantree TO kantree;" && \
        psql "$@" -d kantree -c 'CREATE EXTENSION "unaccent"; CREATE EXTENSION "uuid-ossp"; CREATE EXTENSION "pg_trgm";'
    )
}

check-locale() {
    missing=
    for lc in $LANG $LANGUAGE $LC_CTYPE $LC_NUMERIC $LC_TIME $LC_COLLATE $LC_MONETARY $LC_MESSAGES $LC_PAPER $LC_NAME $LC_ADDRESS $LC_TELEPHONE $LC_MEASUREMENT $LC_IDENTIFICATION $LC_ALL
    do
        if echo $lc | grep 'UTF-8' &>/dev/null; then
            lc="$(echo $lc | cut -d'.' -f1).utf8"
        fi
        if [ -n $lc ] && ! locale -a | grep $lc &>/dev/null; then
            if ! echo $missing | grep $lc &>/dev/null; then
                missing="$missing $lc"
            fi
        fi
    done
    if [ -n "$missing" ]; then
        _echoerr "Some locales are missing: $missing"
        _ask "Update your locales now (Y,n)? " y
        case $ASK_VALUE in
            [Nn]*) ;;
            * ) locale-gen $missing ;;
        esac
    fi
}

check_locale() { # back compat
    check-locale "$@"
}

install-services() {
    check-locale
    _echostep "Installing Redis"
    apt-get install -y redis-server wget
    _checkcmd $? "Redis failed to install"
    _echostep "Installing PostgreSQL 14"
    pgrepo="http://apt.postgresql.org/pub/repos/apt/"
    if ! apt-cache policy | grep "$pgrepo" &> /dev/null; then
        echo "deb $pgrepo $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list
        wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
        apt-get update
    fi
    apt-get install -y postgresql-14 postgresql-contrib-14
    _checkcmd $? "PostgresSQL failed to install"
    service postgresql start
    su -c './platform createdb' postgres
}

install_services() { # back compat
    install-services "$@"
}

install-licensing() {
    _echostep "Installing licensing module..."
    if [ -z "$1" ]; then
        platform="ubuntu-24.04-x86_64"
        if [ -e /etc/lsb-release ]; then
            source /etc/lsb-release
        fi
        if [ "$DISTRIB_ID" == "Ubuntu" ] && [ "$DISTRIB_RELEASE" == "20.04" ]; then
            platform="ubuntu-20.04-x86_64"
        elif [ "$DISTRIB_ID" == "Ubuntu" ] && [ "$DISTRIB_RELEASE" == "22.04" ]; then
            platform="ubuntu-22.04-x86_64"
        fi
        filename="licensing/licensing.$platform.egg"
        if [ ! -e $filename ]; then
            _echoerr "Licensing package not found. Please contact support@kantree.io"
            exit 1
        fi
    else
        filename=$1
    fi
    pyver=$($POETRY_BIN run python -V 2>&1 | cut -d' ' -f2 | cut -d'.' -f1,2)
    unzip -o $filename "*.py" "*.so" -d .venv/lib/python${pyver}/site-packages
    _checkcmd $? "Failed installing licensing module. Please contact support@kantree.io"
}

install_licensing() {  # back compat
    install-licensing "$@"
}

install-venv() {
    _echostep "Installing Poetry..."
    if [ -n "$POETRY_VENV_PATH" ] && [ ! -e $POETRY_VENV_PATH ]; then
        $PYTHON_BIN -m venv $POETRY_VENV_PATH
        _checkcmd $? "Failed creating virtualenv"
        $POETRY_VENV_PATH/bin/pip install -U pip setuptools
        $POETRY_VENV_PATH/bin/pip install poetry
        $POETRY_BIN config virtualenvs.in-project true
    fi
    _echostep "Installing requirements..."
    $POETRY_BIN run pip install setuptools
    $POETRY_BIN install
    _checkcmd $? "Some requirements failed to install"
    if [ -e licensing ]; then
        install-licensing
    fi
}

install_venv() {  # back compat
    install-venv "$@"
}

gen-config() {
    filename=$1
    if [ -z "$filename" ]; then
        _echoerr "Missing target filename"
        exit 1
    fi
    SECRETKEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)

    _ask "Server name [localhost:5000]: " "localhost:5000"; SERVERNAME="$ASK_VALUE"
    if [ -z "$SERVERNAME" ]; then
        SERVERNAME=localhost:5000
    else
        _echotip "If you serve Kantree from your own domain, it is recommended to use a reverse-proxy like Nginx"
    fi

    _ask "Is the server behind https (y,N)? " n
    case $ASK_VALUE in
        [Yy]*)
            SERVERSECURE="true"
            SCHEME="https"
            _echotip "If you want to serve Kantree using HTTPS, you will need a reverse proxy like Nginx"
            ;;
        * )
            SERVERSECURE="false"
            SCHEME="http"
            ;;
    esac

    PUSHDEFAULT=http://localhost:4000
    if [ "$SERVERNAME" != "localhost:5000" ]; then
        PUSHDEFAULT=$SCHEME://$SERVERNAME
    fi
    _ask "Server url for the push server [$PUSHDEFAULT]: " "$PUSHDEFAULT"; PUSHURL="$ASK_VALUE"

    DB_URI=
    while [  -z "$DB_URI" ]; do
        _ask "Postgresql database URI (ie: username:password@hostname:port/dbname): "; DB_URI="$ASK_VALUE"
        if [ -z "$DB_URI" ]; then
            _echoerr "You must provide a database URI"
        fi
    done

    _ask "Redis server URL (ie: redis://[:password]@hostname:port/db) [redis://localhost:6379/0]: " "redis://localhost:6379/0"; REDIS_URI="$ASK_VALUE"

    _ask "Directory where to save uploaded files [./uploads]: " "uploads"; UPLOAD_DIR="$ASK_VALUE"

    _ask "Do you want to configure an SMTP server (Y,n)? " y
    case $ASK_VALUE in
        [Nn]*) ;;
        *)
            _ask "SMTP server hostname [localhost]: " "localhost"; SMTPHOST="$ASK_VALUE"
            _ask "SMTP server port [25]: " "25"; SMTPPORT="$ASK_VALUE"
            _ask "SMTP username (leave blank if none): "; SMTPUSER="$ASK_VALUE"
            _ask "SMTP password (leave blank if none): "; SMTPPASS="$ASK_VALUE"
        ;;
    esac

    cat > $filename <<EOL
secret_key: "${SECRETKEY}"
server_name: ${SERVERNAME}
server_secured: ${SERVERSECURE}
database_uri: "postgresql://${DB_URI}"
redis_uri: "${REDIS_URI}"
push_server_url: "${PUSHURL}"
upload_dir: $UPLOAD_DIR
EOL

    if [ -n "$SMTPHOST" ]; then
        cat >> $filename <<EOL
smtp_server: $SMTPHOST
smtp_port: $SMTPPORT
EOL
        if [ -n "$SMTPUSER" ]; then
            echo "smtp_username: $SMTPUSER" >> $filename
        fi
        if [ -n "$SMTPPASS" ]; then
            echo "smtp_password: $SMTPPASS" >> $filename
        fi
    fi

    _echostep "Configuration file generated: $filename"
}

gen_config() {  # back compat
    gen-config "$@"
}

check-license() {
    _echostep "Checking license"
    if [ ! -e kantree/app.pyx ]; then
        # files are not encrypted
        return
    fi

    $POETRY_BIN run python -c 'import licensing; import sys; sys.exit(licensing.register())'
    err=$?
    if [ $err -ne 0 ]; then
        if [ $err -eq 1 ]; then
            msg="missing license file"
        elif [ $err -eq 3 ]; then
            msg="system date changed"
        elif [ $err -eq 4 ]; then
            msg="license expired"
        elif [ $err -eq 5 ]; then
            msg="MAC address changed"
        elif [ $err -eq 6 ]; then
            msg="unable to register license"
        elif [ $err -eq 7 ]; then
            msg="unable to write on disk"
        else
            msg="an error occured"
        fi
        _echoerr "Invalid license: $msg (error $err)"
        exit 1
    fi
}

check_license() { # back compat
    check-license "$@"
}

check-config() {
    _needvenv
    check-license
    _echostep "Checking configuration file"
    if [ ! -e $CONFIG_FILE ]; then
        _echoerr "Missing configuration file"
        if [ "$1" == "--no-auto-create" ]; then
            exit
        fi
        _ask "Create one now (Y,n)? " y
        case $ASK_VALUE in
            [Nn]*) exit 1;;
            * ) gen-config $CONFIG_FILE;;
        esac
    fi
    err=$($POETRY_BIN run kantree check-config 2>&1)
    _checkcmd $? "$err"
}

check_config() {  # back compat
    check-config "$@"
}

kantree() {
    _needvenv
    $POETRY_BIN run kantree "$@"
    return $?
}

upgrade-db() {
    if [ -z "$KANTREE_SKIP_CONFIG_CHECK" ]; then
        check-config
    fi
    _echostep "Ensuring database schema is up to date"
    kantree db upgrade
    _checkcmd $? "An error occured while upgrading the database"
}

upgrade_db() { # back compat
    upgrade-db "$@"
}

create-admin() {
    username=$1; shift
    email=$1; shift
    if [ -z "$username" ]; then
        _ask "Username [admin]" "admin"; username="$ASK_VALUE"
    fi
    if [ -z "$email" ]; then
        _ask "Email [admin@localhost]" "admin@localhost"; email="$ASK_VALUE"
    fi
    kantree create-user --admin $username $email "$@"
    _checkcmd $? "An error occured while creating the admin user"
}

create_admin() { # back compat
    create-admin "$@"
}

make-admin() {
    kantree make-admin $*
}

make_admin() { # back compat
    make-admin "$@"
}

init() {
    _banner
cat <<EOL
 Welcome to Kantree!
 The installation process will check your system,
 generate a configuration file and setup a Python
 virtualenv for Kantree.

 You will need an Internet connection to perform
 these tasks.
EOL
    echo -e "\e[1m"
cat <<EOL
 IMPORTANT: make sure you have added your license
 file at the root of your installation.

EOL
    echo -e "\e[0m"
    sleep 5
    check-system
    install-venv
    upgrade-db
    _ask "Create administrator user now (N,y)? " n
    case $ASK_VALUE in
        [Yy]*) create-admin;;
        * );;
    esac
    _echostep "Successfully initialized Kantree!"
}

upgrade() {
    _banner
    _echostep "Upgrading Kantree"
    check-system
    install-venv
    upgrade-db
    check-config --no-auto-create
    _echostep "Successfully upgraded Kantree!"
}

init-node() {
    _banner
    _echostep "Setuping node installation"
    check-system
    install-venv
    check-config --no-auto-create
    _echostep "Successfully initialized Kantree on this node!"
}

init_node() { # back compat
    init-node
}

version() {
    kantree version
}

run() {
    _needvenv
    if [ -z "$KANTREE_SKIP_CONFIG_CHECK" ]; then
        check_config
    fi
    _echostep "Starting Kantree processes (Ctrl-C to exit)..."
    kantree run "$@"
}

upgrade-db-and-run() {
    upgrade-db && run
}

kill-zombie-push-processes() {
    for pid in $(ps aux | grep "frasco.push.server" | tr -s " " | cut -d" " -f2)
    do
        kill -9 $pid
    done
}

help() {
    _banner
cat <<EOL
 Usage: ./platform <command>

 Available commands:
  - init: Initialize kantree
  - upgrade-db: Upgrade the database
  - run: Run Kantree in the foreground
  - version: Print the version number
  - help: Print this message

EOL
}

$CMD $*
cd $prev_path
