#!/bin/bash
#
# Script to help user manage their local environment

##
# Constant Declarations
##
PLATFORM=$(uname)
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
YELLOW=$(tput setaf 3)
BLUE=$(tput setaf 4)
MAGENTA=$(tput setaf 5)
CYAN=$(tput setaf 6)
WHITE=$(tput setaf 7)
BOLD=$(tput bold)
UNDERLINE=$(tput smul)
CLEAR=$(tput sgr0)
I4="    "
if [[ $SHELL == *"zsh"* ]]; then
    SHELL_RC="$HOME/.zshrc"
else
    SHELL_RC="$HOME/.bashrc"
fi

declare -a PYTHON_VERS=('3.13.5' '3.12.4' '3.11.6' '3.10.12')

CACHE_DIR=$HOME/.antilles
DEV_SETUP_PATH=$CACHE_DIR/dev_setup.txt
LAST_UPDATE_CHECK_PATH=$CACHE_DIR/.caliper_admin_update_check
ENV_VARS_PATH=$CACHE_DIR/sdk_env_vars.sh
SET_VIRTUALENV_PATH=$CACHE_DIR/set_virtualenv.sh
RESTART_SHELL_ON_EXIT=0
IS_Q2_INTERNAL=0


##
# Color Functions
##
ColorRed(){
    echo -ne $RED$1$CLEAR
}
ColorGreen(){
    echo -ne $GREEN$1$CLEAR
}
ColorYellow(){
	echo -ne $YELLOW$1$CLEAR
}
ColorBlue(){
	echo -ne $BLUE$1$CLEAR
}
ColorMagenta(){
	echo -ne $MAGENTA$1$CLEAR
}
ColorCyan(){
	echo -ne $CYAN$1$CLEAR
}
ColorWhite(){
	echo -ne $WHITE$1$CLEAR
}
Bold(){
    echo -ne $BOLD$1$CLEAR
}
Underline(){
    echo -ne $UNDERLINE$1$CLEAR
}


trap exit_with_cleanup SIGINT


#######################################
# Cleanup up environment and restart shell on exit.
# Arguments:
#   The code to return.
# Returns:
#   exit code given or -1
#######################################
function exit_with_cleanup() {
    local exit_code=$1
    if [[ -z "$exit_code" ]]; then
        exit_code=-1
    fi
    if [[ -n $SHELL ]]; then
        if [[ $RESTART_SHELL_ON_EXIT = 1 ]]; then
            ColorYellow 'Restarting shell to apply changes\n'
            exec "$SHELL"
        fi
    fi
    exit "$exit_code"
}

#######################################
# Simple wrapper that calls dev_setup() based on the IS_Q2_INTERNAL variable
#######################################
function get_dev_settings() {
    if [ ! -f "$DEV_SETUP_PATH" ]; then
        touch "$DEV_SETUP_PATH"
    fi
    IS_Q2_INTERNAL=$(_get_dev_setup_var "IS_Q2_INTERNAL")
    if [[ -z "$IS_Q2_INTERNAL" ]]; then
        dev_setup
    fi
}

function dev_setup() {
    if [[ $PLATFORM == 'Darwin' ]]; then
        _first_time_mac_setup
    fi
    ynprompt_n "Are you working inside Q2's internal network? (This can be changed later)"
    local yn=$?
    if [[ yn -eq 1 ]]; then
        _set_dev_setup_var "IS_Q2_INTERNAL" 1
        ColorGreen "Q2 Internal set to true\n"
    else
        _set_dev_setup_var "IS_Q2_INTERNAL" 0
        ColorGreen "Q2 Internal set to false\n"
    fi
    get_dev_settings
}

function _first_time_mac_setup() {
    local ROOT_CERTS_FILE="/etc/ssl/keychain-root-certs.pem"
    local ROOT_CERTS_KEYCHAIN="/System/Library/Keychains/SystemRootCertificates.keychain"
    local SYSTEM_CERTS_FILE="/etc/ssl/keychain-system-certs.pem"
    local SYSTEM_CERTS_KEYCHAIN="/Library/Keychains/System.keychain"
    local COMBO_CERTS_FILE="/etc/ssl/keychain-certs.pem"

    if [[ ! -f "$COMBO_CERTS_FILE" ]]; then
        read -r -p "For python to use your mac's keychain certificates for calling https endpoints, we need to create a PEM file it can reference. Press Enter to continue."
        sudo security export -t certs -p -k "$SYSTEM_CERTS_KEYCHAIN" -o "$SYSTEM_CERTS_FILE"
        sudo chmod 0444 $SYSTEM_CERTS_FILE
        sudo security export -t certs -p -k "$ROOT_CERTS_KEYCHAIN" -o "$ROOT_CERTS_FILE"
        sudo chmod 0444 $ROOT_CERTS_FILE
        sudo bash -c "cat $ROOT_CERTS_FILE $SYSTEM_CERTS_FILE > $COMBO_CERTS_FILE"
        sudo chmod 0444 "$COMBO_CERTS_FILE"
        echo
    fi
}

function _get_dev_setup_var() {
    local SETUP_VAR="$1"
    VAL=$(grep "$SETUP_VAR=" "$DEV_SETUP_PATH" | awk -F '=' '{print $2}')
    echo "$VAL"
}

function _set_dev_setup_var() {
    local SETUP_VAR="$1"
    local NEW_VALUE="$2"
    local VAL
    VAL=$(_get_dev_setup_var "$SETUP_VAR")

    if [[ -n "$VAL" ]]; then
        if [[ "$PLATFORM" == 'Linux' ]]; then
            sed -i "s/$SETUP_VAR=.*/$SETUP_VAR=$NEW_VALUE/" $DEV_SETUP_PATH
        elif [[ "$PLATFORM" == 'Darwin' ]]; then
            sed -i '' "s/$SETUP_VAR=.*/$SETUP_VAR=$NEW_VALUE/" $DEV_SETUP_PATH
        fi
    else
        echo "$SETUP_VAR=$NEW_VALUE" >> "$DEV_SETUP_PATH"
    fi
}

function menu() {
    menuHeader
    get_dev_settings
    if [ $PLATFORM == 'Linux' ]; then
        linuxMenu
    elif [ $PLATFORM == 'Darwin' ]; then
        macMenu
    fi
}

function menuHeader() {

    echo -ne "$(ColorYellow '
    Welcome to the Caliper SDK admin tool. (version 1.6.0)\n
    This attempts to bring as many of the initial setup\n
    and maintenance tasks as possible into a single script.\n\n')"

}

function linuxMenu() {

echo -ne "
$I4 $(ColorYellow '1)')$(ColorCyan ' Express (All in one)')
$I4 $(ColorYellow '2)')$(ColorCyan ' Install System Dependencies')
$I4 $(ColorYellow '3)')$(ColorCyan ' Switch Stack')
$I4 $(ColorYellow '4)')$(ColorCyan ' Start New Project')
$I4 $(ColorYellow '5)')$(ColorCyan ' Switch Project')
$I4 $(ColorYellow '6)')$(ColorCyan ' Rebuild python')
$I4 $(ColorYellow '7)')$(ColorCyan ' Rebuild Virtual Environment')
$I4 $(ColorYellow '8)')$(ColorCyan ' Start memcached')
$I4 $(ColorYellow '9)')$(ColorCyan ' Sync Certs from Windows Host')
$I4 $(ColorYellow '10)')$(ColorCyan ' Switch Internal/External')
$I4 $(ColorYellow '11)')$(ColorCyan ' Configure for Q2 VPN (Internal)')
$I4 $(ColorYellow '12)')$(ColorCyan ' Verify Connectivity')
$I4 $(ColorYellow '13)')$(ColorCyan ' Update This Script')
$I4 $(ColorYellow '0)')$(ColorCyan ' Exit')

$(ColorYellow 'Choose an Option: ')"

    read -r a
    case $a in
        1) express;;
        2) install_system_dependencies;;
        3) switch_stack;;
        4) start_new_project;;
        5) switch_project;;
        6) get_generic_python;;
        7) rebuild_virtual_environment; autoenable_env;;
        8) start_memcached;;
        9) sync_system_certs;;
        10) dev_setup; switch_stack;;
        11) setup_q2_vpn;;
        12) verify_connectivity;;
        13) update;;
        0) exit_with_cleanup;;
        *) echo -ne " $(ColorRed ''"Please enter a number from the list above"'')"; menu ;;
    esac
    sleep 1

}

function macMenu() {

echo -ne "
$I4 $(ColorYellow '1)')$(ColorCyan ' Switch Stack')
$I4 $(ColorYellow '2)')$(ColorCyan ' Start New Project')
$I4 $(ColorYellow '3)')$(ColorCyan ' Switch Project')
$I4 $(ColorYellow '4)')$(ColorCyan ' Rebuild Python Version')
$I4 $(ColorYellow '5)')$(ColorCyan ' Rebuild Virtual Environment')
$I4 $(ColorYellow '6)')$(ColorCyan ' Start Memcached')
$I4 $(ColorYellow '7)')$(ColorCyan ' Install Frontend Dependencies')
$I4 $(ColorYellow '8)')$(ColorCyan ' Switch Internal/External')
$I4 $(ColorYellow '9)')$(ColorCyan ' Update This Script')
$I4 $(ColorYellow '0)')$(ColorCyan ' Exit')

$(ColorYellow 'Choose an Option: ')"

    read -r a
    case $a in
        1) switch_stack;;
        2) start_new_project;;
        3) switch_project;;
        4) get_generic_python;;
        5) rebuild_virtual_environment; autoenable_env;;
        6) start_memcached;;
        7) install_frontend_dependencies;;
        8) dev_setup; switch_stack;;
        9) update;;
        0) exit_with_cleanup 0;;
        *) echo -ne " $(ColorRed ''"Please enter a number from the list above"'')"; menu;;
    esac
    sleep 1

}

function check_for_updates() {
    if [ ! -s $LAST_UPDATE_CHECK_PATH ]; then
        if [ "$(uname -s)" = 'Darwin' ]; then
            date -v -25H '+%s' > $LAST_UPDATE_CHECK_PATH
        else
            date --date='25 hours ago' '+%s' > $LAST_UPDATE_CHECK_PATH
        fi
    fi
    last_check=$(cat $LAST_UPDATE_CHECK_PATH)
    current_time=$(date '+%s')
    diff=$(($current_time - $last_check))

    # No more than once every 24 hours
    if [ $diff -gt 86400 ]; then
        echo $current_time > $LAST_UPDATE_CHECK_PATH
        update_local
        update
    fi
}

function express() {
    echo -e "$(ColorCyan 'This will do the following:')

$I4 $(ColorYellow '- Install system dependencies')
$I4 $(ColorYellow '- Point to your development stack')
$I4 $(ColorYellow '- Clone your repo')

Press $(ColorYellow 'Enter') and hold on to your butts"

    read -r a
    echo "$(ColorCyan '===Installing system Dependencies===')"
    install_system_dependencies
    echo "$(ColorCyan '===Setting Stack===')"
    switch_stack
    echo "$(ColorCyan '===Git Repo===')"
    start_new_project
    echo "$(ColorCyan '===Memcached===')"
    start_memcached
    echo
    echo "$(ColorCyan '===Wrap up===')"
    echo "You are all set!"
    echo "To begin working, you can type $(ColorYellow 'source '"$SHELL_RC"'')"
    exit
}

function verify_connectivity() {

    echo -n "Q2Developer Connectivity: "
    curl -s https://q2developer.com > /dev/null
    if [[ $? -eq 0 ]]; then
        echo "$(ColorGreen 'Ok')"
    else
        echo "$(ColorRed 'Fail')"
    fi

    echo -n "Npm Connectivity: "
    if [[ ! $(npm ping 2>&1 | grep WARN) ]]; then
        echo "$(ColorGreen 'Ok')"
    else
        echo "$(ColorRed 'Fail')"
    fi
}

function install_system_dependencies() {
    sudo apt-get update
    sudo apt-get install -y \
        curl \
        git \
        gcc \
        g++ \
        gnupg2 \
        libffi-dev \
        librdkafka-dev \
        libreadline-dev \
        libsqlite3-dev \
        libxml2-dev \
        libxmlsec1-dev \
        lsof \
        make \
        memcached \
        net-tools \
        pkg-config \
        unixodbc-dev \
        vim \
        wget \
        xz-utils \
        zlib1g-dev

    install_frontend_dependencies
}

function install_frontend_dependencies() {
    echo "$(ColorYellow 'The SDK will run yarn to build frontend assets. Our default toolset is nvm/node/yarn. If you already have a way to compile frontend code with yarn, you can configure yourself manually.')"
    ynprompt_y "Would you like this tool to setup nvm/node/yarn for you?"
    local yn=$?
    if [[ yn -eq 0 ]]; then
        exit 0
    fi
    # Install Frontend build prerequisites
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
    mkdir -p ~/.nvm
    echo yarn > ~/.nvm/default-packages
    # shellcheck disable=SC1090
    source ~/.nvm/nvm.sh
    local already_configured
    if [ $PLATFORM == 'Linux' ]; then
        already_configured=$(grep "cafile" "$HOME/.npmrc")
        if [[ -z $already_configured ]]; then
            echo 'cafile=/etc/ssl/certs/ca-certificates.crt' >> $HOME/.npmrc
        fi
    fi

    nvm install 20
    nvm alias default 20
    nvm use 20
    RESTART_SHELL_ON_EXIT=1
}

function switch_stack() {
    echo
    mkdir -p $CACHE_DIR

    if [[ ! -z $1 ]]; then
        stack_name=$1
    else
        echo
    fi

    local ardentfs_url="https://stack.q2developer.com/sdk/ardentfs/ardent"
    if [[ $IS_Q2_INTERNAL -eq 1 ]]; then
        if [[ -z "$stack_name" ]]; then
            echo "You are set to internal mode and are therefore shown ``$(Underline 'Internal')`` stacks. To switch, select ``$(ColorYellow 'Switch Internal/External')`` from the menu"
            read -r -p "$(ColorYellow 'Stack name in corporate fabio (ex. proserv44, 44, bdcs): ')" stack_name
        fi
        local hq_url="http://fabio-hq-lb.service.q2consul-corp/sdk/${stack_name}/hq"
        local ardent_url="http://fabio-online-lb.service.q2consul-corp/sdk/${stack_name}/ardent"
        local pip_index_url='https://aus-bin-prd-00.q2dc.local/artifactory/api/pypi/wedge-virtpy/simple'
        local pip_trusted_host='aus-bin-prd-00.q2dc.local'
        local bounce_url="http://consul.service.q2consul-corp:8500/v1/kv/restart/sdk-${stack_name}_dc0-corp"
        local sql_server_host="q2-db-dev-002.cdcveqkpvo5f.us-east-1.rds.amazonaws.com"
        local sql_server_user="SDKDB"
        local sql_server_pw="Q2L@bsP@ssword4U"
    else
        if [[ -z "$stack_name" ]]; then
            echo "You are set to external mode and are therefore shown ``$(Underline 'AWS')`` stacks. To switch, select ``$(ColorYellow 'Switch Internal/External')`` from the menu"
            read -r -p "$(ColorYellow 'Q2 provided stack name: ')" stack_name
        fi
        local hq_url="https://stack.q2developer.com/sdk/${stack_name}/hq"
        local ardent_url="https://stack.q2developer.com/sdk/${stack_name}/ardent"
        local pip_index_url='https://devpi.q2developer.com/root/q2/+simple'
        local bounce_url="https://sdk-shared-dev.q2devstack.com/9999/bounce_hq/${stack_name}"
        local sql_server_host='127.0.0.1'
        local sql_server_user="dev_sa"
        local sql_server_pw="q2success"
    fi

cat << EOF > "$ENV_VARS_PATH"
export Q2SDK_ARDENT_URL=$ardent_url
export Q2SDK_ARDENTFS_URL=$ardentfs_url
export PIP_INDEX_URL=$pip_index_url
export PIP_TRUSTED_HOST=$pip_trusted_host
export Q2SDK_IP_WHITELIST=52.86.164.26,216.117.81.84
export STACKNAME=$stack_name
export ANTILLES_SERVER_PORT=1980
export SERVE_ASSETS_ON_LOCALHOST=True
export DEV_SERVER_DB_PORT=1433
export Q2SDK_SLASH_FOR_INSTALL_PORT=True
export HQ_URL=$hq_url
export CSR_USER=q2api
export CSR_PWD=2q
export ABA=022222222
export SQL_SERVER_HOST=$sql_server_host
export SQL_SERVER_NAME=Q2DB_sdk_$stack_name
export SQL_SERVER_USER=$sql_server_user
export SQL_SERVER_PWD=$sql_server_pw
export CACHE_HOST=localhost
export Q2SDK_USE_INC_HQ=False
export Q2SDK_VAULT_LOCAL_DIR=~/.vaultdir
export NOMAD_SECRETS_DIR=~/.nomadsecrets
export Q2SDK_STACK_BOUNCE_URL=$bounce_url
export Q2SDK_DEV_SOCKET_URL=local-dev-api.q2developer.com
EOF

    if [[ $PLATFORM == 'Darwin' ]]; then
        echo "export SSL_CERT_FILE=/etc/ssl/keychain-certs.pem" >> "$ENV_VARS_PATH"
        echo "export REQUESTS_CA_BUNDLE=/etc/ssl/keychain-certs.pem" >> "$ENV_VARS_PATH"
    else
        echo "export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt" >> "$ENV_VARS_PATH"
        echo "export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt" >> "$ENV_VARS_PATH"
    fi

    ColorGreen "Environment variables written to $ENV_VARS_PATH\n"
    echo "  --Note: You can edit this file directly if necessary"
    local already_in_shell
    already_in_shell=$(grep "source \~/.antilles/sdk_env_vars.sh" "$SHELL_RC")

    if [[ -z $already_in_shell ]]; then
        ynprompt_y "Add to $SHELL_RC?"
        local yn=$?
        if [[ yn -eq 1 ]]; then
            echo "Adding ``$(ColorYellow 'source ~/.antilles/sdk_env_vars.sh')`` to ``$(ColorYellow ''"$SHELL_RC"'')``"
            echo "source $CACHE_DIR/sdk_env_vars.sh" >> $SHELL_RC
        fi
    fi
    source $CACHE_DIR/sdk_env_vars.sh
    mkdir -p $NOMAD_SECRETS_DIR
    mkdir -p $Q2SDK_VAULT_LOCAL_DIR
    RESTART_SHELL_ON_EXIT=1
}

function start_new_project() {
    echo -ne "
$I4 $(ColorYellow '1)')$(ColorCyan ' Clone from existing Git repo (Probably what you want)')
$I4 $(ColorYellow '2)')$(ColorCyan ' Start from scratch')

$(ColorYellow 'Choose an Option: ')"

    read -r a
    case $a in
        1) select_repo_type;;
        2) prompt_for_directory; initialize_git_repo; rebuild_virtual_environment; prompt_for_alias; autoenable_env;;
        *) echo -ne " $(ColorRed ''"Please enter a number from the list above"'')"; start_new_project ;;
    esac
}

function select_repo_type() {
    echo -ne "
$I4 $(ColorYellow '1)')$(ColorCyan ' Clone from a custom Git url')
$I4 $(ColorYellow '2)')$(ColorCyan ' Clone from an existing sdk example Git repo')

$(ColorYellow 'Choose an Option: ')"

    read -r a
    case $a in
        1) custom_git_url;;
        2) select_from_list;;
        *) echo -ne " $(ColorRed ''"Please enter a number from the list above"'')"; select_repo_type ;;
    esac
}

function custom_git_url() {
    read -r -p 'Git url: ' GIT_URL

    if [[ $GIT_URL == '' ]]; then
         echo -e " $(ColorRed ''"Git url cannot be empty string"'')"; custom_git_url
    else
        clone_git_repo $GIT_URL
    fi
}

function select_from_list() {
    echo -ne "
$I4 $(ColorYellow '1)')$(ColorCyan ' uux-payload-and-API-example')
$I4 $(ColorYellow '2)')$(ColorCyan ' sso-example')

$(ColorYellow 'Choose a repo to clone: ')"

    read -r a
    case $a in
        1) EXAMPLE="uux-payload-and-api-example.git";;
        2) EXAMPLE="sso-example.git";;
        *) echo -ne " $(ColorRed ''"Please enter a number from the list above"'')"; select_from_list ;;
    esac

    GIT_URL='git@code.q2developer.com:Q2Antilles/sdk-examples/'
    GIT_URL+=$EXAMPLE

    clone_git_repo $GIT_URL
}

function clone_git_repo() {
    DEST_DIR=$(basename $1 | sed -E 's/.git$//')
    mkdir -p $DEST_DIR-tmp

    if [[ $1 == https* ]]; then
        ynprompt_y "This clones using https, we suggest using ssh method. Would you still like to continue?";
        local yn=$?
        if [[ yn -ne 1 ]]; then
           custom_git_url
        fi

    else
      if [[ ! -f ~/.ssh/id_rsa.pub ]]; then
          echo "Let's generate you a SSH keypair to authenticate you to your git provider"
          echo "Press Enter to generate a public/private ssh keypair."
          ssh-keygen -f ~/.ssh/id_rsa -P ''
          echo "$(ColorYellow 'Add this ssh-key into your git provider interface:')"
          cat ~/.ssh/id_rsa.pub
          read -r -p "Press Enter when ready"
      fi
    fi

    local current_name current_email
    current_name=$(git config user.name)
    current_email=$(git config user.email)

    if [[ -z $current_name || -z $current_email ]]; then
        echo "Git is going to need to know your name/email for commit messages:"
        read -r -p "Name: " name
        read -r -p "Email: " email
        git config --global user.name "$name"
        git config --global user.email "$email"
    fi

    # Clone into a temporary location
    git clone $1 $DEST_DIR-tmp

    if [[ $? -ne 0 && $1 == ssh* ]]; then
        echo "$(ColorRed 'Please make sure you have added your public ssh key to your git provider (Gitlab/Bitbucket/etc)')"
      exit 1
    fi

    # Clone was successful. Backup the existing sdk directory if it exists
    if [[ -d $DEST_DIR ]]; then
        BACKUP_DIR=$DEST_DIR.bak
        if [[ -d $DEST_DIR.bak ]]; then
            i=1
            while [[ -d $DEST_DIR-$i.bak ]]; do
                let i++
            done
            BACKUP_DIR=$DEST_DIR-$i.bak
        fi
        echo "$(ColorYellow ''"Creating backup of existing $DEST_DIR directory into $BACKUP_DIR"'')"
        mv -v $DEST_DIR $BACKUP_DIR
    fi

    # Move temporary location to final destination
    mv -v $DEST_DIR-tmp $DEST_DIR

    if [[ -d $BACKUP_DIR/.vscode ]]; then
        cp -rv $BACKUP_DIR/.vscode $DEST_DIR
    fi

    cd $DEST_DIR

    echo "Repo cloned to $(ColorYellow ./$DEST_DIR)"

    ynprompt_y "Would you like to take this opportunity to upgrade this repo to the latest CaliperSDK version?"
    local yn=$?
    if [[ yn -eq 1 ]]; then
        delete_sdk_requirement
        rebuild_virtual_environment
        autoenable_env
        echo "q2-sdk==$(.env/bin/q2 --version)" >> requirements.txt
    else
        rebuild_virtual_environment
        autoenable_env
    fi

}

function switch_project() {
    PROJECT_OPTIONS=()

    # Going to parent file to be able to also switch to projects under it
    if [[ -d .antilles ]]; then
        cd ..
    fi
    PARENT_DIR=$PWD

    # Search the current directory for valid projects to switch to
    # Searches down a depth of 1 and only looks for directories
    for entry in $(find . -maxdepth 1 -type d)
    do
        if [[ -d ${entry}/.antilles ]]
        then
            VAL=${entry#*/}
            PROJECT_OPTIONS+=($VAL)
        fi
    done

    # Checking to see if the options array is empty
    if [[ -z $PROJECT_OPTIONS ]]; then
        echo "$(ColorRed 'There are no projects to switch to from this position, move to a directory with extensions')"
        exit 1
    else
        # Prompt user to choose which directory to switch to
        echo "$(ColorYellow ''"Please choose a project:"'')"
            select PROJECT in "${PROJECT_OPTIONS[@]}"; do
                [[ -n $PROJECT ]] || { echo "Invalid choice. Please try again." >&2; continue; }
            break # valid choice was made; exit prompt.
            done
    fi

    echo "$(ColorYellow ''"Switching to ${PROJECT} project"'') "
    DEST_DIR=$PARENT_DIR/$PROJECT

    cd $DEST_DIR

    # Only works if user agrees to change their default dir to the project they are moving to
    ynprompt_y "This will change your default directory when you login, would you still like to continue?"
    local yn=$?
    if [[ yn -eq 1 ]]; then
        if [[ -d .env ]]; then
            write_to_bashrc
            echo "Switched to $(ColorYellow $DEST_DIR)"
        else
            rebuild_virtual_environment
            write_to_bashrc
        fi
    else
        echo "Exiting"
    fi
}

function delete_sdk_requirement() {
    if [[ "$PLATFORM" == 'Linux' ]]; then
        sed -i '/^q2-sdk/d' requirements.txt
    elif [[ "$PLATFORM" == 'Darwin' ]]; then
        sed -i '' '/^q2-sdk/d' requirements.txt
    fi
}

function write_to_bashrc(){
    echo "cd $PWD && source .env/bin/activate" > $SET_VIRTUALENV_PATH
    local already_in_shell
    already_in_shell=$(grep "source \~/.antilles/set_virtualenv.sh" "$SHELL_RC")
    if [[ -z $already_in_shell ]]; then
        echo "Adding ``$(ColorYellow 'source '"$SET_VIRTUALENV_PATH"'')`` to ``$(ColorYellow ''"$SHELL_RC"'')``"
        echo "source $SET_VIRTUALENV_PATH" >> "$SHELL_RC"
    fi
    RESTART_SHELL_ON_EXIT=1
}

# shellcheck disable=SC2120
function autoenable_env() {
    local DEST_DIR=$1

    local activate_str="source .env/bin/activate"
    local activate_alias="alias activate='source \$PWD/.env/bin/activate'"
    if grep -qF "$activate_alias" "$ENV_VARS_PATH"; then
        activate_str="activate"
    fi

    ynprompt_y 'Would you like your shell to always start here with this environment activated?'
    local yn=$?
    if [[ yn -eq 1 ]]; then
        write_to_bashrc
    else
        if [ -z $DEST_DIR ]; then
            echo "To begin working here, type ``$(ColorYellow ''"$activate_str"'')``"
        else
            echo "To begin working here, type ``$(ColorYellow ''"cd $DEST_DIR && $activate_str"'')``"
        fi
    fi
}

function prompt_for_directory(){
    array=("~" "/" "-")
    read -r -p "$(ColorYellow ''"Please provide a name for the directory you want to build the project into:"'' ) [sdk] " new_dir
    new_dir=${new_dir:-sdk}
    if [[ $(echo "${array[@]}" | fgrep -w "$new_dir") ]]
    then
      echo "Directories with names (~,/,-) are not valid"
      prompt_for_directory;
    fi
    mkdir -p "$new_dir"
    new_dir_path=$(realpath "$new_dir")
    if [[ "$new_dir_path" == "$HOME" ]]
    then
      echo "Creating project in home directory [$new_dir] is not allowed."
      prompt_for_directory;
    else
      cd "$new_dir_path"
    fi

}

function initialize_git_repo() {
    if ! [ -d ".git" ]; then
        git init .
    fi
}

function prompt_for_alias() {
    local activate_alias="alias activate='source \$PWD/.env/bin/activate'"
    if ! grep -qF "$activate_alias" "$ENV_VARS_PATH"; then
        ynprompt_n 'Would you like to add `activate` as an alias for activating your virtual environments?'
        local add_alias=$?
        if [[ add_alias -eq 1 ]]; then
            echo -en "\n" >> "$ENV_VARS_PATH"
            echo "$activate_alias" >> "$ENV_VARS_PATH"
            echo "alias 'activate' has been added to $ENV_VARS_PATH"
            RESTART_SHELL_ON_EXIT=1
        fi
    fi
}

function get_generic_python() {
    echo -ne "
$I4 $(ColorYellow '1)')$(ColorCyan ' 3.13')
$I4 $(ColorYellow '2)')$(ColorCyan ' 3.12')
$I4 $(ColorYellow '3)')$(ColorCyan ' 3.11')
$I4 $(ColorYellow '4)')$(ColorCyan ' 3.10')

$(ColorYellow 'Choose an Option: ')"

    read -r a
    case $a in
        1) get_python 3.13;;
        2) get_python 3.12;;
        3) get_python 3.11;;
        4) get_python 3.10;;
        *) echo -ne " $(ColorRed ''"Please enter a number from the list above"'')" ; get_generic_python ;;
    esac
}

function rebuild_virtual_environment() {
    SDK_VERSION=$(get_sdk_ver)
    PYTHON_BIN=$(get_python_binary)
    if [[ -z $SDK_VERSION ]]; then
        echo "SDK version: latest requires $PYTHON_BIN"
    else
        echo "SDK version: $SDK_VERSION requires $PYTHON_BIN"
    fi
    if [[ -d .env ]]; then
        echo 'Deleting existing virtualenv: .env'
        sleep 2
        rm -rfv .env
    fi

    if [[ ! -f /usr/local/bin/$PYTHON_BIN ]]; then
        local skip_install=0;
        if [[ `which $PYTHON_BIN` ]]; then
            ynprompt_y "Detected a version of $PYTHON_BIN already, though not installed using caliper_admin. Use anyway?"
            local yn=$?
            if [[ yn -eq 1 ]]; then
                skip_install=1;
            fi
        fi

        if [[ skip_install -eq 0 ]]; then
            read -r -p "$(ColorYellow ''"$PYTHON_BIN is not installed. Press Enter to install"'')"
            HERE=$PWD
            get_python "$(echo $PYTHON_BIN | sed -e 's/python//')"
            cd $HERE
            PYTHON_BIN=$(get_python_binary)
        fi
    fi

    read -r -p "$(ColorYellow ''"Please provide a name for your virtual environment prompt:"'' ) [.env] " venv
    venv=${venv:-.env}

    echo "``$(ColorYellow 'Creating virtual environment: ') $venv`` "
    $PYTHON_BIN -m venv .env --prompt="$venv"
    .env/bin/pip install --upgrade pip
    .env/bin/pip install wheel
    .env/bin/pip install MarkupSafe==2.0.1

    if [[ -f 'requirements_dev.txt' ]]; then
        .env/bin/pip install -r requirements_dev.txt
    fi
    if [[ -f 'requirements.txt' ]]; then
        .env/bin/pip install -r requirements.txt
    fi
    if [[ ! -f '.env/bin/q2' ]]; then
        echo "``$(ColorYellow 'No SDK installation detected. Doing basic setup...')"
        sleep 2
        .env/bin/pip install q2-sdk
        .env/bin/q2 setup
    fi

    .env/bin/q2 install_completion --assume-yes
}

function start_memcached() {
    if [ $PLATFORM == 'Linux' ]; then
        if [[ ! `which memcached` ]]; then \
            echo "``$(ColorRed 'Memcached not installed. Perhaps try "sudo apt install memcached"')"
            exit 1
        fi
    elif [ $PLATFORM == 'Darwin' ]; then
        if [[ ! `which memcached` ]]; then \
            echo "``$(ColorRed 'Memcached not installed. Perhaps try "brew install memcached".')"
            exit 1
        fi
    fi
    memcached -d
    if [[ $? -eq 0 ]]
    then
      echo "$(ColorGreen 'Memcached started!')"
    fi
}

function sync_system_certs() {
    if [[ ! -L "/usr/local/share/ca-certificates/extra" ]]; then
        sudo ln -s $HOME/host-certificates /usr/local/share/ca-certificates/extra
    fi

    rm -rf $HOME/host-certificates
    mkdir $HOME/host-certificates
    cat <<- "EOF" > /tmp/sync_system_certs.ps1
        $StoreToDir = "host-certificates"
        $InsertLineBreaks=1

        Get-ChildItem -Recurse cert: `
          | Where-Object { $_ -is [System.Security.Cryptography.X509Certificates.X509Certificate2] } `
          | ForEach-Object {
            $name = $_.Subject -replace '[\W]', '_'
            $oPem=new-object System.Text.StringBuilder
            [void]$oPem.AppendLine("-----BEGIN CERTIFICATE-----")
            [void]$oPem.AppendLine([System.Convert]::ToBase64String($_.RawData,$InsertLineBreaks))
            [void]$oPem.AppendLine("-----END CERTIFICATE-----")

            $path = "{0}\{1}.crt" -f $StoreToDir,$name

            # the exported list of certificates contains certificates with similar subject
            # let's put them in separate indexed files
            $idx = 0
            While (Test-Path $path) {
              $idx++
              $path = "{0}\{1}--{2}.crt" -f $StoreToDir,$name,$idx
            }
            # If ($idx -gt 0) {
            #   $path
            # }

            # # TODO unfortunately same certificates duplicates each other
            # # it's better to add a check for duplicates just here

            $oPem.toString() | add-content $path
            #Exit(0)
          }
	EOF
    HERE=$PWD
    cd $HOME
    powershell.exe -ExecutionPolicy Bypass /tmp/sync_system_certs.ps1
    cd $HERE
    sudo update-ca-certificates
}

function get_python() {
    if [ $1 == '3.13' ]; then
        VERSION=${PYTHON_VERS[0]}
    elif [ $1 == '3.12' ]; then
        VERSION=${PYTHON_VERS[1]}
    elif [ $1 == '3.11' ]; then
        VERSION=${PYTHON_VERS[2]}
    elif [ $1 == '3.10' ]; then
        VERSION=${PYTHON_VERS[3]}
    fi

    if [ $PLATFORM != 'Linux' ]; then
        DOWNLOAD_URL=$(echo "https://www.python.org/ftp/python/$VERSION/python-$VERSION-macos11.pkg");
        echo "Download python version $VERSION from $(ColorYellow $DOWNLOAD_URL)";
        exit 0;
    fi

    MINOR_VERSION=$(echo $VERSION | sed -E 's#\.[[:digit:]]+$##')
    CPUS=$(grep -c ^processor /proc/cpuinfo)

    cd /tmp
    wget https://www.python.org/ftp/python/$VERSION/Python-$VERSION.tar.xz
    tar xvf Python-$VERSION.tar.xz
    cd Python-$VERSION
    ./configure
    make -j $CPUS
    sudo make altinstall
    cd ..
    rm -rfv ./Python-$VERSION*
    sudo "pip$MINOR_VERSION" install wheel
}

# functions.sh
function verlte() {
    [[ "$1" = $(echo -e "$1\n$2" | sort -V | head -n1) ]]
}

function verlt() {
    [[ "$1" = "$2" ]] && return 1 || verlte $1 $2
}

# shellcheck disable=SC2120
function get_sdk_ver() {
    local MIN_SDK_VERSION=$1
    local CUR_SDK_VERSION
    if [[ -f requirements.txt ]]; then
        if [ "$PLATFORM" == 'Linux' ]; then
            CUR_SDK_VERSION=$(grep q2-sdk requirements.txt | grep -oP '\d+\.\d+\.\d+')
        elif [ "$PLATFORM" == 'Darwin' ]; then
            CUR_SDK_VERSION=$(grep q2-sdk requirements.txt | grep -oE '\d+\.\d+\.\d+')
        fi
    else
        CUR_SDK_VERSION='latest'
    fi

    if verlte "$CUR_SDK_VERSION" "$MIN_SDK_VERSION"; then
        echo "$MIN_SDK_VERSION"
    else
        echo "$CUR_SDK_VERSION"
    fi
}

function get_python_binary() {
    local PYTHON_3_13_MIN=2.280.0
    local PYTHON_3_12_MIN=2.224.0
    local PYTHON_3_11_MIN=2.177.0
    local PYTHON_3_10_MIN=2.134.0
    local SDK_VERSION
    SDK_VERSION=$(get_sdk_ver)
    if verlt "$SDK_VERSION" "$PYTHON_3_13_MIN"; then
        if verlt "$SDK_VERSION" "$PYTHON_3_12_MIN"; then
            if verlt "$SDK_VERSION" "$PYTHON_3_11_MIN"; then
                echo 'python3.10'
            else
                echo 'python3.11'
            fi
        else
            echo 'python3.12'
        fi
    else
        echo 'python3.13'
    fi
}

function ynprompt_y() {
    read -r -p "$(ColorYellow ''"$1"'' ) [Y/n] " yn
    yn=${yn:-y}
    case $yn in
        [Yy]* ) return 1;;
        [Nn]* ) return 0;;
        * ) echo "Please answer Y or N.";;
    esac
}

function ynprompt_n() {
    read -r -p "$(ColorYellow ''"$1"'' ) [y/N] " yn
    yn=${yn:-n}
    case $yn in
        [Yy]* ) return 1;;
        [Nn]* ) return 0;;
        * ) echo "Please answer Y or N.";;
    esac
}

function update() {
    local SCRIPT_DIR
    SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
    curl -s https://q2sdk-utilities.s3.amazonaws.com/caliper_admin -qo /tmp/caliper_admin
    diff -q "$SCRIPT_DIR/caliper_admin" /tmp/caliper_admin
    if [[ $? -ne 0 ]]; then
        ynprompt_y 'Updates are available! Update now?'
        local yn=$?
        if [[ yn -eq 1 ]]; then
            chmod +x /tmp/caliper_admin
            sudo cp -pv /tmp/caliper_admin "$SCRIPT_DIR/caliper_admin"
            ColorGreen 'Updated!'
            exit 0
        fi
    fi
}

function update_local() {
    if [[ -f $ENV_VARS_PATH ]]; then
        if [[ $(grep 'Q2SDK_DEV_SOCKET_URL=sdk-shared-dev' "$ENV_VARS_PATH") ]]; then
            ColorYellow 'Detected deprecated DEV_SOCKET_URL (sdk-shared-dev.q2devstack.com). Changing to new one (local-dev-api.q2developer.com)'
            CURRENT_STACK=$(grep STACKNAME "$ENV_VARS_PATH" | grep -Eo '[a-z]+$')
            switch_stack "$CURRENT_STACK"
        fi
    fi
}

function setup_q2_vpn() {
    set -e

    echo -e "$(ColorCyan 'This is meant to be used by internal devs (Q2 employees) only to work in harmony with the Q2 provided VPN service. It will do the following:')

$I4 $(ColorYellow '- Create a scheduled task that will enable this whenever the Cisco VPN connects (Requires an administrator prompt)')
$I4 $(ColorYellow '- Make a powershell script that moves Cisco Anyconnect network interface below the WSL network in resolution order')
$I4 $(ColorYellow "- Disable WSL's autogeneration of resolv.conf")
$I4 $(ColorYellow "- Make our own resolv.conf that uses Q2's DNS nameservers")
$I4 $(ColorYellow "- Prefer Windows's networking layer to WSL's")

Press $(ColorYellow 'Enter') to begin"

    cd
    read -r a

    is_admin=$(powershell.exe -Command "(([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -contains 'S-1-5-32-544')")
    if [[ $is_admin == "False"* ]]; then
        echo -e "$(ColorRed 'Your Linux shell must be running as Administrator in order to make these changes.')"
        exit 1
    fi

    WINDOWS_USERNAME=$(powershell.exe  -Command "(Get-WMIObject -class Win32_ComputerSystem | select username).username" | sed 's#AUSTIN\\##' | tr -d '[:space:]' )

    echo 'Enable scheduled task to set wsl networking on Cisco VPN connect'
    cat <<- "EOF" > /tmp/metric.ps1
		Get-NetAdapter | Where-Object {$_.InterfaceDescription -Match "Cisco AnyConnect"} | Set-NetIPInterface -InterfaceMetric 4000
		Get-NetIPInterface -InterfaceAlias "vEthernet (WSL)" | Set-NetIPInterface -InterfaceMetric 1
	EOF
    mkdir -p /mnt/c/Scripts
    cp /tmp/metric.ps1 /mnt/c/Scripts/metric.ps1

    cat <<- "EOF" > /tmp/Task-Powershell-event.ps1
		$Action = New-ScheduledTaskAction -Execute 'Powershell.exe' -WorkingDirectory C:\scripts -Argument "-ExecutionPolicy Bypass -File C:\scripts\metric.ps1"
		$CIMTriggerClass = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskEventTrigger
		$Trigger = New-CimInstance -CimClass $CIMTriggerClass -ClientOnly
		$Trigger.Subscription =
		@"
		<QueryList><Query Id="0" Path="Cisco Secure Client - AnyConnect VPN"><Select Path="Cisco Secure Client - AnyConnect VPN">*[System[Provider[@Name='csc_vpnagent'] and EventID=2054]]</Select></Query></QueryList>
		"@
		$Trigger.Enabled = $True
		Register-ScheduledTask -Action $Action -Trigger $Trigger -TaskName "Configure WSL Q2Vpn" -Description 'test' -User 'System' -Force -RunLevel Highest
	EOF
    powershell.exe -ExecutionPolicy Unrestricted /tmp/Task-Powershell-event.ps1

    echo 'Disable autogeneration of resolv.conf'
    cat <<- EOF > /tmp/wsl.conf
		[network]
		generateResolvConf = false
	EOF
    sudo mv /tmp/wsl.conf /etc/wsl.conf

    echo 'Replace resolv.conf for DNS resolution'
    cat <<- EOF > /tmp/resolv.conf
		nameserver 10.115.4.210
		nameserver 10.215.4.210
		nameserver 192.168.1.240
		nameserver 192.168.1.1
		search austin.q2technology.com
	EOF
    sudo mv /tmp/resolv.conf /etc/resolv.conf

    echo "Use Windows networking layer inside WSL"
    cat <<- EOF > /tmp/.wslconfig
		[wsl2]
		localhostforwarding=true
	EOF
    mv /tmp/.wslconfig "/mnt/c/Users/$WINDOWS_USERNAME/.wslconfig"

    ColorGreen 'Complete!'
}

mkdir -p "$CACHE_DIR"
check_for_updates
menu
exit_with_cleanup 0
