#!/bin/bash

set -Ee

CHECK_KEYS=false
INSERT_KEYS=false
UNIT_FILE=false
SKIP_BUILD=false
SET_ENVIRONMENT=false
VALIDATE=false
EXECUTABLE_PATH="/usr/bin/"
SPECIFICATION_PATH="/etc/ghost/"
BASE_PATH="/var/lib/ghost"
SPECIFICATION_NAME="casper"
TARGET="release"

CURRENT_PATH=$(pwd)
CURRENT_SCRIPT=$(realpath "$0")
SCRIPT_FOLDER=$(dirname "$CURRENT_SCRIPT")
PROJECT_FOLDER=("$SCRIPT_FOLDER/..")

final() {
    cd $CURRENT_PATH
    if [[ $1 -eq 1 ]]; then
        echo "[-] error occured during execution"
    else
        echo "[+] execution finished"
    fi
    exit $1
}

prompt() {
    while true; do
        printf "$1 [y/N]: "
        read yn
        case $yn in
            [Yy]* ) return 0;;
            * ) return 1;;
        esac
    done
}

sanity_check() {
    secret_seed=$(ghost key inspect --scheme="$1" "$2" | grep "Secret seed" | awk '{ print $3 }')
    account_id=$(ghost key inspect --scheme="$1" "$2" | grep "Account ID" | awk '{ print $3 }')

    echo "[+] inspected account id for $3: $account_id"
    num_keys=$(grep $account_id "$PROJECT_FOLDER/service/ghosties" | wc -l)
    num_types=$(grep $account_id "$PROJECT_FOLDER/service/ghosties" | grep $3 | wc -l)
    if [ $num_keys = 1 ] && [ $num_types = 1 ]; then
        echo "[+] local $3 key found in 'ghosties' with correct key type"
    else
        echo "[-] inspected account id not found on 'ghosties' file or wrong key type" 
        exit 1
    fi
}

genesis_check() {
    secret_seed=$(ghost key inspect --scheme="$1" "$2" | grep "Secret seed" | awk '{ print $3 }')
    account_id=$(ghost key inspect --scheme="$1" "$2" | grep "Account ID" | awk '{ print $3 }')

    num_keys=$(grep ${account_id:2} "$PROJECT_FOLDER/service/chain-specs/casper.json" | wc -l)
    if [ ! $num_keys = 0 ]; then
        echo "[+] found in genesis block"
    else
        echo "[-] not yet in genesis block" 
    fi
    echo
}

extract_seed() {
    name_with_spaces=$(echo $1 | tr '-' ' ')
    read -p "[?] path to the file with $name_with_spaces: (default: /etc/ghost/$1) " seed_path
    seed_path="${seed_path:-/etc/ghost/$1}"
    if [ ! -f $seed_path ]; then
        echo "[-] path to $name_with_spaces is not valid"
    fi
    seed=$(cat $seed_path)
    echo $seed
}

help() {
    echo -e "Ghost Node Build automation tool. Helper for Ghost Node environment preparation.\n"
    echo -e "With no OPTION nothing will happen, possible OPTION:\n"
    echo -e "-i, --set-environment\n\tSet up rust environment."
    echo -e "-u, --unit-file\n\tCreation of systemd unit file."
    echo -e "-m, --make-global\n\tStore compiled ghost executable and chain specification globally."
    echo -e "-a, --set-arguments\n\tPrepare CLI arguments for running ghost node."
    echo -e "-k, --check-keys\n\tCheck if your keys are already included in 'ghosties' file."
    echo -e "-y, --insert-keys\n\tInsert session keys to the keystore via JSON RPC."
    echo -e "-r, --release\n\tCompile node with '--release' flag."
    echo -e "-v, --validate\n\tManipulate '--validate' flag in CLI_ARGS."
    echo -e "-p, --profile\n\tCompile node with '--profile [PROFILE]' flag."
    echo -e "-f, --features\n\tCompilation features '--features=\"FEATURE1,FEATURE2\"'"
    echo -e "-e, --executable-path\n\tPath to the executable ('/usr/lib/' is default)."
    echo -e "-s, --base-path\n\tPath to the folder with chain database ('/var/lib/ghost' is default)."
    echo -e "-c, --specification-path\n\tPath to specification ('/etc/ghost' is default)."
    echo -e "-n, --specification-name\n\tSpecification name to be used ('casper' is default)."
    echo -e "-h, --help\n\tPrints help information."
}

trap 'final "$?"' EXIT

clear
echo "  ____ _               _     _   _           _"
echo " / ___| |__   ___  ___| |_  | \ | | ___   __| | ___"
echo "| |  _| '_ \ / _ \/ __| __| |  \| |/ _ \ / _' |/ _ \\"
echo "| |_| | | | | (_) \__ \ |_  | |\  | (_) | (_| |  __/"
echo " \____|_| |_|\___/|___/\__| |_| \_|\___/ \__,_|\___|"
echo -e "\nCreated by st1nky (stinky@ghostchain.io)"
echo -e "Repository: https://git.ghostchain.io/ghostchain/ghost-node"
echo -e "Usage: starter.sh [OPTION]\n"

while [ $# -gt 0 ]; do
    case "$1" in
        --set-environment|-i)
            SET_ENVIRONMENT=true
            ;;
        --unit-file|-u)
            UNIT_FILE=true
            ;;
        --make-global|-m)
            MAKE_GLOBAL=true
            ;;
        --set-arguments|-a)
            ARGUMENTS=true
            ;;
        --check-keys|-k)
            CHECK_KEYS=true
            ;;
        --insert-keys|-k)
            INSERT_KEYS=true
            ;;
        --release|-r)
            RELEASE="--release"
            TARGET="release"
            ;;
        --validate|-v)
            VALIDATE=true
            ;;
        --profile*|-p*)
            if [[ "$1" != *=* ]]; then shift; fi
            RELEASE="--profile=${1#*=}"
            TARGET="${1#*=}"
            ;;
        --feature*|-f*)
            if [[ "$1" != *=* ]]; then shift; fi
            FEATURES="--features=${1#*=}"
            ;;
        --executable-path*|-e*)
            if [[ "$1" != *=* ]]; then shift; fi
            EXECUTABLE_PATH=$(echo ${1#*=}/ | tr -s /)
            ;;
        --specification-path*|-c*)
            if [[ "$1" != *=* ]]; then shift; fi
            SPECIFICATION_PATH=$(echo ${1#*=}/ | tr -s /)
            ;;
        --base-path*|-?)
            if [[ "$1" != *=* ]]; then shift; fi
            BASE_PATH=$(echo ${1#*=}/ | tr -s /)
            ;;
        --specification-name*|-n*) 
            if [[ "$1" != *=* ]]; then shift; fi
            SPECIFICATION_NAME="${1#*=}"
            ;;
        --help|-h)
            help
            exit 0
            ;;
        *)
            help
            exit 1
            ;;
    esac
    shift
done

if [[ $SET_ENVIRONMENT = true ]]; then
    echo -e "\n"
    echo "WARNING!!! THIS IS HIGHLY EXPERIMENTAL FLAG, USE IT ONLY ON YOUR"
    echo "OWN RISK! NOT EVERY SYSTEM CHECKED AND PROVED TO BE WORKING AS"
    echo "EXPECTED! FOR THOSE WHO FOUND BUG OR SOME INCONSISTENCY FEEL"
    echo "FREE TO FILL ISSUE ON https://git.ghostchain.io/ghostchain/ghost-node/issues"
    echo -e "\n"
    if prompt "[?] are you brave enough?"; then 
        echo "[+] you were warned, I hope you know what you're doing" 
    else
        echo "[-] aborting environment setup"
        exit 1
    fi

    if command -v rustc >/dev/null 2>&1; then
        echo "[+] rust already installed"
    else
        os_name=$(uname -s)
        cd $HOME
        if [ "$os_name" = "Darwin" ]; then
            echo "[+] detected MacOS. installing dependencies via homebrew"

            if command -v brew >/dev/null 2>&1; then
                echo "[+] already installed: $(brew --version)"
            else
                if prompt "[?] do you want to install homebrew?"; then 
                    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
                else
                    echo "[-] cannot continue without homebrew"
                    exit 1
                fi
            fi

            brew update

            if prompt "[?] install git, cmake, openssl and protobuf?"; then
                brew install git cmake openssl protobuf
            else
                echo "[!] assuming git, cmake, openssl and protobuf are present"
            fi
        elif [ "$os_name" == "Linux" ]; then
            distro=$(cat /etc/*-release | tr '[:upper:]' '[:lower:]' | grep -Poi '(debian|ubuntu|arch|fedora|opensuse)' | uniq | head -n 1)
            echo $distro

            if [ "$distro" = "ubuntu" ]; then
                echo "[+] ubuntu detected, using apt to install dependencies"
                sudo apt update
                sudo apt install --assume-yes git make clang curl libssl-dev protobuf-compiler
            elif [ "$distro" = "debian" ]; then
                echo "[+] debian detected, using apt to install dependencies"
                sudo apt update
                sudo apt install --assume-yes git make clang curl libssl-dev llvm libudev-dev protobuf-compiler
            elif [ "$distro" = "arch" ]; then
                echo "[+] arch linux detected, using pacman to install dependencies"
                sudo pacman -Syu --needed --no-confirm curl git clang make protobuf
            elif [ "$distro" = "fedora" ]; then
                echo "[+] fedora detected, using dnf to install dependencies"
                sudo dnf update --assumeyes
                sudo dnf install --assumeyes clang curl git openssl-devel make protobuf-compiler perl
            elif [ "$distro" = "opensuse" ]; then
                echo "[+] openSUSE detected, using zypper to install dependencies"
                sudo zypper install --no-confirm clang gcc gcc-c++ curl git openssl-devel llvm-devel libudev-devel make awk protobuf-devel
            else
                if prompt "[?] unknown linux distribution, unable to install dependencies. continue anyway?"; then
                    echo "[+] proceeding with unknown linux distribution"
                else
                    echo "[-] aborting with uknown distribution"
                    exit 1
                fi
            fi
        else
            echo "[-] unknown operating system"
            exit 1
        fi
    fi

    if prompt "[?] setup the rust environment (e.g. WASM support)?"; then
        rustup default stable
        rustup update
        rustup target add wasm32-unknown-unknown
        rustup component add rust-src
    fi
fi

if [[ ! -z $RELEASE ]]; then
    if prompt "[?] 'cargo build $RELEASE $FEATURES' is what you want?"; then 
        cd $PROJECT_FOLDER
        echo "[+] Starting build in 3 seconds..."
        sleep 3
        cargo build $RELEASE $FEATURES
    fi
fi

if [[ $VALIDATE = true ]]; then
    cli_args=$(cat /etc/default/ghost)
    if prompt "[?] turn on validator mode?"; then
        if [[ $cli_args == *"--validator"* ]]; then
            echo "[+] '--validator' flag already exists in CLI_ARGS, check /etc/default/ghost"
        else
            cli_args="${cli_args%?} --validator\""
            echo "[+] '--validator' flag inserted to CLI_ARGS, check /etc/default/ghost"
        fi
    else
        cli_args=${cli_args// --validator/}
        echo "[+] '--validator' flag removed"
    fi

    echo "[+] trying to store CLI updated arguments to '/etc/default/ghost'"
    sudo echo "$cli_args" > /etc/default/ghost
    echo "[+] updated CLI arguments stored in '/etc/default/ghost'"

    if prompt "[?] do you want to restart 'ghost-node.service' now?"; then
        sudo systemctl status ghost-node.service
        echo "[+] 'ghost-node.service' succefully restarted, new CLI_ARGS applied"
    else
        echo "[+] don't forget to restart ghost node service manually"
    fi
fi

if [[ $MAKE_GLOBAL = true ]]; then
    cd $PROJECT_FOLDER
    echo "[+] trying to copy executable to '$EXECUTABLE_PATH'"
    sudo cp target/$TARGET/ghost $EXECUTABLE_PATH
    cp service/chain-specs/$SPECIFICATION_NAME.json $SPECIFICATION_PATH

    echo "[+] ghost executable copied in '$EXECUTABLE_PATH' from '$TARGET'"
    echo "[+] specification '$SPECIFICATION_NAME.json' copied to '$SPECIFICATION_PATH'"
fi

if [[ $ARGUMENTS = true ]]; then
    echo "[+] setting-up default CLI arguments"
    CLI_ARGS=()

    read -p "[?] specify p2p protocol TCP port (default: 30333): " port
    CLI_ARGS+=("--port=${port:-30333}")

    read -p "[?] specify JSON-RPC server TCP port: (default: 9945): " rpc_port
    CLI_ARGS+=("--rpc-port=${rpc_port:-9945}")

    read -p "[?] specify the chain specification (default: /etc/ghost/casper.json): " chain
    CLI_ARGS+=("--chain=${chain:-/etc/ghost/casper.json}")

    read -p "[?] specify node's secret key file for p2p networking (default: /etc/ghost/node-key): " node_key
    CLI_ARGS+=("--node-key-file=${node_key:-/etc/ghost/node-key}")

    read -p "[?] specify name for the node (default: RANDOM_NAME): " node_name
    if [[ ! -z $node_name ]]; then 
        CLI_ARGS+=("--name='$node_name'")
    fi

    CLI_ARGS+=("--validator")
    if prompt "[?] disable validator mode?"; then 
        unset CLI_ARGS[-1]
    fi

    if prompt "[?] enable prometheus?"; then 
        read -p "[?] specify prometheus exporter TCP port: (default: 9615)" prometheus_port
        CLI_ARGS+=("--prometheus-port=${prometheus_port:-9615}")
    else
        CLI_ARGS+=("--no-prometheus")
    fi

    read -p "[?] bootnode if any: " bootnodes
    if [ ! -z $bootnodes ]; then
        CLI_ARGS+=("--bootnodes=$bootnodes")
    fi

    read -p "[?] address that other nodes will use to connect: (default: current ip)" public_addr
    if [ ! -z $public_addr ]; then
        CLI_ARGS+=("--public-addr=$public_addr")
    fi


    # default for now
    CLI_ARGS+=("--telemetry-url='wss://telemetry.ghostchain.io/submit/ 9'")
    CLI_ARGS+=("--base-path=$BASE_PATH")
    CLI_ARGS+=("--state-pruning=archive")
    CLI_ARGS+=("--blocks-pruning=archive")
    CLI_ARGS+=("--rpc-methods=auto")
    CLI_ARGS+=("--no-private-ip")
    CLI_ARGS+=("--no-mdns")

    echo "[+] trying to save new node arguments"
    echo "GHOST_CLI_ARGS=\"$(IFS=' '; echo "${CLI_ARGS[*]}")\"" > /tmp/ghost
    sudo cp /tmp/ghost /etc/default/ghost
    rm /tmp/ghost
    echo "[+] new CLI arguments stored in '/etc/default/ghost'"
    cat /etc/default/ghost
fi

if [ $CHECK_KEYS = true ]; then
    seed=$(extract_seed "wallet-key")
    sanity_check "sr25519" $seed "wallet"
    genesis_check "sr25519" $seed "wallet"

    seed=$(extract_seed "stash-key")
    sanity_check "sr25519" $seed "stash"
    genesis_check "sr25519" $seed "stash"

    seed=$(extract_seed "session-key")
    if [ $INSERT_KEYS = true ]; then
        read -p "[?] JSON RPC endpoint to the node: (default: localhost:9945) " rpc_endpoint
        rpc_endpoint="${rpc_endpoint:-localhost:9945}"
    fi

    for type in $(echo audi babe gran slow); do
        echo "[+] parsing session key for [$type]"
        scheme="sr25519"
        if [ $type = "gran" ]; then
            scheme="ed25519"
        fi
        
        sanity_check $scheme "$seed//$type" $type
        genesis_check $scheme "$seed//$type" $type
        if [ $INSERT_KEYS = true ]; then
            echo "[+] trying to make an 'author_insertKey' RPC call to $rpc_endpoint..."
            curl --location $rpc_endpoint \
                --header "Content-Type: application/json" \
                --data '{ "id":1, "jsonrpc":"2.0", "method":"author_insertKey", "params": ["'"$type"'", "'"$secret_seed"'", "'"$account_id"'"] }'
        fi
        echo
    done
fi

if [ $UNIT_FILE = true ]; then
    cd $SCRIPT_FOLDER
    read -p "[?] name for the unit file (default: ghost-node.service) " unit_name
    if [ -z $unit_name ]; then
        unit_name="ghost-node"
    fi
    unit_name=$(echo "$unit_name" | sed -e "s/.service//g")
    unit_name="$unit_name.service"

    user_name="ghost"
    if id $user_name > /dev/null 2>&1; then
        echo "[+] user ghost found, continue for the user ghost"
    else
        echo "[!] user ghost not found"
        if prompt "[?] do you want to create ghost user? (NOT RECOMMENDED: current $(whoami))"; then 
            sudo useradd --system --create-home $user_name
        else
            user_name=$(whoami)
        fi
    fi

    if [ ! -d $BASE_PATH ]; then
        echo "[+] create folder for the node at '$BASE_PATH'"
        sudo mkdir $BASE_PATH
    fi

    if [ -z "$(stat -c "%U %G" $BASE_PATH | grep $user_name)" ]; then
        echo "[+] make $user_name owner of $BASE_PATH"
        sudo chown -R "$user_name:" $BASE_PATH
    fi
    
    cp packaging/template.service /tmp/$unit_name
    sed -i -e "s/User=ghost/User=$user_name/g" /tmp/$unit_name
    sed -i -e "s#/ReadWritePaths=/var/lib/ghost#/ReadWritePaths=$BASE_PATH#g" /tmp/$unit_name

    echo "[+] prepare unit file for the $unit_name"
    sudo cp packaging/template.service /etc/systemd/system/$unit_name
    echo "[+] reloading systemd because of updated unit file"
    sudo systemctl daemon-reload

    if prompt "[?] do you want to start the $unit_name?"; then 
        sudo systemctl restart $unit_name
    fi

    if prompt "[?] do you want to enable the $unit_name?"; then 
        sudo systemctl enable $unit_name
    fi
fi