#!/bin/bash

## Colors for output
YELLOW="$(printf '\033[0;33m')"
GREEN="$(printf '\033[0;32m')"
BLUE="$(printf '\033[0;34m')"
CYAN="$(printf '\033[0;36m')"
RED="$(printf '\033[1;31m')"
NC="$(printf '\033[0m')"  # No color.
readonly YELLOW GREEN BLUE CYAN RED NC

## Other constants.
readonly BOT_EXECUTABLE="EllieBot"
readonly BIN_DIR="ellie"
readonly BACKUP_DIR="ellie_backups"
readonly CREDS_FILE="$BIN_DIR/data/creds.yml"
readonly CREDS_EXAMPLE_FILE="$BIN_DIR/data/creds_example.yml"

####
# Identify the system architecture and OS, and return a string that represents it.
#
# RETURNS:
#   - linux-x64: For Linux x86_64.
#   - linux-arm64: For Linux aarch64 or arm64.
#   - osx-x64: For macOS x86_64.
#   - osx-arm64: For macOS arm64.
#   - unsupported: For all other architectures or operating systems.
get_arch() {
    local os
    os=$(uname -s)
    local arch
    arch=$(uname -m)

    case "$os" in
    Linux)
        case "$arch" in
        x86_64) echo "linux-x64" ;;
        aarch64 | arm64) echo "linux-arm64" ;;
        *) echo "unsupported" ;;
        esac
        ;;
    Darwin)
        case "$arch" in
        x86_64) echo "osx-x64" ;;
        arm64) echo "osx-arm64" ;;
        *) echo "unsupported" ;;
        esac
        ;;
    *)
        echo "unsupported"
        ;;
    esac
}

backup_bot() {
    if [ -d $BIN_DIR/data/ ]; then
        if [ ! -d $BACKUP_DIR/ ]; then
            mkdir $BACKUP_DIR
        fi

        date_now=$(date +%s)
        cp -r $BIN_DIR/data "$BACKUP_DIR/$date_now-data"

        echo "${BLUE}Your current data "
    fi
}

# TODO: Still needs to move data from the old directory to the new one.
####
# Downloads the latest release, extracts it, and sets up EllieBot's directory. If
# the directory already exists, it will be renamed to `ellie.old`. If
# `ellie.old` already exists, it will be deleted.
#
# PARAMETERS:
#   - $1: version (Required)
#       - The release version to download.
#
# RETURNS:
#   - 1: On failure of some operation.
install_bot() {
    local version="$1"
    local arch
    arch=$(get_arch)
    local output="ellie-new"
    local tar_url="https://toastielab.dev/toastie_t0ast/elliebot/releases/download/${version}/ellie-${arch}.tar.gz"

    [[ $arch == "unsupported" ]] && { echo "${RED}ERROR: Unsupported architecture${NC}" >&2; return 1; }

    echo "${BLUE}Downloading '${version}' for '${arch}'...${NC}"

    [[ -d $output ]] && rm -r ./$output
    mkdir ./$output

    if ! curl -L "$tar_url" | tar -xzf - -C ./$output --strip-components=1; then
        echo "${RED}ERROR: Failed to download or extract the archive${NC}" >&2
        rm -r ./$output
        return 1
    fi

    if [[ -d $BIN_DIR ]]; then
        backup_bot
        mv "$BIN_DIR" "${BIN_DIR}-old"
    else
        echo "${BLUE}EllieBot not installed. Installing for the first time.${NC}"
    fi

    mv ./$output $BIN_DIR

    if [[ -d "${BIN_DIR}-old" ]]; then
        echo "${BLUE}Copying over data folder...${NC}"
        [[ -d "${BIN_DIR}-old/data/" ]] && cp -rf "${BIN_DIR}-old/data/"* "$BIN_DIR/data/"
    fi

    chmod +x "${BIN_DIR}/${BOT_EXECUTABLE}"
    echo "${GREEN}Installation complete!${NC}"

    # Clean up any leftover folders
    [[ -d $output ]] && rm -r ./$output
    [[ -d "${BIN_DIR}-old" ]] && rm -r "${BIN_DIR}-old"
}

# TODO: Somehow get the bot version, and
#       paint available versions with color
#       RED: older (downgrade), not recommended
#       GREEN: newer (upgrade)
#       BLUE: same (reinstall)

####
# Display a list of available versions, and prompt the user to select one to install.
install_submenu() {
    local versions

    # get versions from /repos/tags toastielab endpoint
    mapfile -t versions < <(curl -s https://toastielab.dev/api/v1/repos/toastie_t0ast/elliebot/tags | grep -oP '"ref": "refs/tags/\K[^"]+')

    echo "${CYAN}Select version to install:${NC}"
    select version in "${versions[@]}"; do
        if [[ -n $version ]]; then
            install_bot "$version"
            break
        else
            echo "${RED}ERROR: Invalid selection${NC}"
        fi
    done
}

####
# Determines whether the 'token' field in the credentials file is set.
#
# NOTE:
#   This is not a comprehensive check for the validity of the token; it only verifies
#   that the token field is not empty.
#
# RETURNS:
#   - 0: If the token is set.
#   - 1: If the token is not set.
is_token_set() {
    if grep -Eq '^token: '\'\''' "$CREDS_FILE"; then
        return 1
    else
        return 0
    fi
}

####
# Verify that the bot is installed, the token in the 'creds.yml' file is set, and then
# run the bot.
#
# RETURNS:
#   - 1: If the bot is not installed, the executable is not found, or the token is not
#       set.
run_bot() {
    ## Ensure that the bot is installed.
    if [[ ! -d $BIN_DIR ]]; then
        echo "${RED}ERROR: EllieBot not installed. Please install it first.${NC}" >&2
        return 1
    fi

    ## Ensures that the executable exists.
    if [[ ! -f $BIN_DIR/$BOT_EXECUTABLE ]]; then
        echo "${RED}ERROR: EllieBot executable not found${NC}" >&2
        return 1
    fi

    ## Create the creds file if it doesn't exist.
    if [[ ! -f $CREDS_FILE ]]; then
        if [[ ! -f $CREDS_EXAMPLE_FILE ]]; then
            echo "${RED}ERROR: 'creds_xample.yml'not found. Make sure the bot is installed${NC}" >&2
            return 1
        fi
        cp -f "$CREDS_EXAMPLE_FILE" "$CREDS_FILE"
    fi

    ## Ensure that the token is set in the creds file or check if env var bot_token is set
    if ! is_token_set && [[ -z "${bot_token}" ]]; then
        echo "${RED}ERROR: Bot token not set. Please set it in the credentials file or as an environment variable.${NC}" >&2
        return 1
    fi

    echo "${BLUE}Attempting to run EllieBot...${NC}"
    pushd "$BIN_DIR" >/dev/null || return 1
    ./$BOT_EXECUTABLE
    popd >/dev/null || return 1
}

install_music_dependencies() {
    curl -L -o e-musicreq.sh https://toastielab.dev/toastie_t0ast/ellie-bash-installer/raw/branch/v6/e-prereq.sh || {
        echo "ERROR: Failed to download the music dependencies installer"
        return 1
    }

    if [[ ! -f e-musicreq.sh ]]; then
        echo "${RED}ERROR: Failed to download the music dependencies installer${NC}" >&2
        return 1
    fi

    chmod +x e-musicreq.sh
    ./e-musicreq.sh
    rm e-musicreq.sh
}

edit_creds() {
    if [[ ! -f $CREDS_FILE ]]; then
        cp $CREDS_EXAMPLE_FILE $CREDS_FILE
    fi

    # ask the user to input the token
    echo "Please input your token: "
    echo ""
    read -r token

    # check if the token is not empty
    if [[ -z "$token" ]]; then
        echo "ERROR: Invalid token." >&2
        return 1
    fi

    # replace the token in the creds file
    # by finding a line which starts with 'token: ' and replacing it
    sed -i "s/token: .*/token: \"$token\"/" $CREDS_FILE
}

migrate_from_v5() {

    # 0. Ensure that the bot is installed
    if [[ ! -d $BIN_DIR ]]; then
        echo "${RED}ERROR: EllieBot v6 not installed. Please install it first and then you'll be able to migrate.${NC}"
        return 1
    fi

    # 1. Check if there is a elliebot/output folder
    if [ ! -d "elliebot/output" ]; then
        echo "${RED}ERROR: No v5 installation found.${NC}"
        return 1
    fi

    # 2. Copy over the entirety of the data folder and overwrite
    if [ -d "elliebot/output/data/" ]; then
        # strings are no longer there, nor do we need them
        rm -rf elliebot/output/data/strings
        cp -rf elliebot/output/data/* ellie/data/

        # move the data folder to avoid double migration
        mv elliebot/output/data elliebot/output/data.old
    fi

    # Done
    echo "${GREEN}Migration successful! Your v6 bot is now in the 'ellie' folder.${NC}"
    MIGRATED=1
}

####
# Display the main menu options.
display_menu() {
    clear
    echo "${BLUE}===== EllieBot Release Installer =====${NC}"
    echo "1. Install"
    echo "2. Run"
    echo "3. Add Token"
    echo "4. Install music dependencies"
    echo "5. Exit"
    if [[ ! $MIGRATED -eq 1 && -d "elliebot/output/data" ]]; then
        echo "6. Migrate v5 from-source version"
    fi
    echo -n "${CYAN}Enter your choice:${NC} "
}

MIGRATED=1
# check if there is a elliebot/output/data folder
if [ ! -d "elliebot/output/data" ]; then
    MIGRATED=0
fi

while true; do
    display_menu
    read -r choice
    case $choice in
    1) install_submenu ;;
    2) run_bot ;;
    3) edit_creds ;;
    4) install_music_dependencies ;;
    5)
        echo "${GREEN}Exiting...${NC}"
        exit 0
        ;;
    6)
        if [[ $MIGRATED -eq 1 || ! -d "elliebot/output/data" ]]; then
            echo "${YELLOW}WARNING: Nothing to migrate. You must have a v5 elliebot/output folder!${NC}" >&2
            break
        fi
        migrate_from_v5
        ;;
    *) echo "${RED}ERROR: Invalid option${NC}" ;;
    esac

    echo "${CYAN}Press [Enter] to continue${NC}"
    read -r
done