#!/bin/bash

version="1.2.4"

BACKUP_FOLDER="backup_"
PROTECTED_FILES=("config.js" "config.6.js" "config.json")
PROTECTED_FOLDERS=("resources")
EXCLUDE_PTOCTED_PATH=("node_modules" "configuration_rewriters")

PROXY_POD=$(kubectl get pods -n sisense -l 'app=management' | awk '/'management'/ {print $1;}')
DEFAULT_CONTAINER="management"
package_root_path=$(dirname "$(pwd)")

# Substring plugin name from root folder of package.
plugin_name=$(basename $(dirname "$(pwd)") | grep -oP '\.[[:alpha:]]+\.' | tr '.' ' ' | xargs)
plugin_sources_folder="addon_sources"
storage_root_path="/opt/sisense/storage"

declare -A destination_paths
destination_paths[client_side_addons]="${storage_root_path}/plugins"
destination_paths[server_side_addons]="${storage_root_path}/serverSidePlugins"
destination_paths[external_addons]="${storage_root_path}/external-plugins/apiPlugins/plugins"
destination_paths[middlewares]="${storage_root_path}/external-plugins/apiPlugins/middlewares"
destination_paths[configuration_rewriters]="${storage_root_path}/external-plugins/apiPlugins/configuration_rewriters"
destination_paths[translations]="${storage_root_path}/translations"

declare -A destination_paths_files
destination_paths_files[configuration_rewriters]="true"
destination_paths_files[middlewares]="true"

declare -a inspect_order=("configuration_rewriters" "middlewares" "client_side_addons" "server_side_addons" "external_addons" "translations")
declare -a COMPONENTS_WITH_CONFIG=("client_side_addons" "server_side_addons" "external_addons")
declare -a REMOVE_BY_NAME_COMPONENT=("translations")
declare -A component_member_map

# --------------------- PUBLIC ---------------------

# check sources for components and their members.
# create associative array [key]=(value).
# where key - component, value - it members.
function inspect_sources() {
    try_unzip_sources
    clearComponentMemberMap

    for component in ${!destination_paths[@]}; do
        local component_path="$package_root_path/$plugin_sources_folder/$component"
        # check if folder exist.
        if [ -d "$component_path" ]; then
            local path_regex="[0-9a-zA-Z_\-]+"
            local type="d"
            local search_path=""$component_path"/"

            if [ ${destination_paths_files[$component]} ]; then
                path_regex=".+\..+"
                type="f"
            fi

            local regex=""$component_path"/$path_regex"
            # Search for posible solution component by regex in sources.
            while IFS= read -r -d $'\0' clm; do
                local component_list_member_name=$(basename "$clm")

                if [[ component_member_map[$component] ]]; then
                    component_member_map[$component]="${component_member_map[$component]} $component_list_member_name"
                else
                    component_member_map[$component]="$component_list_member_name"
                fi

            done < <(find "$search_path" -maxdepth 1 -type $type -regextype egrep -regex "$regex" -print0)
        fi
    done
}

function clearComponentMemberMap() {
    for i in ${!component_member_map[@]}; do
        unset component_member_map[$i]
    done
}

function removeJasperReportReadyExchange() {
    local RABBITMQ_POD="sisense-rabbitmq-0"
    local report_ready_exchange_url="http://127.0.0.1:15672/api/exchanges/%2f/jasperReports%2freportReady"
    local remove_exchange_command="curl -X DELETE --user guest:guest --url $report_ready_exchange_url"
    local exchange_removed_success_msg=" > RabbitMQ 'jasperReports/freportReady' exchange successfully removed\n"
    local exchange_removed_error_msg=" > Error occured during removing RabbitMQ 'jasperReports/freportReady' exchange\n"

    kubectl exec --stdin --tty -n sisense $RABBITMQ_POD -- $remove_exchange_command &> /dev/null && {
        printf "$exchange_removed_success_msg"
    } || {
        printf "$exchange_removed_error_msg"
    }
}

function install() {
    echo " > Add-on components installation started. Please wait, this can take some time..."
    echo " "
    local skip_restart=$1
    local is_update=$2

    if [[ $plugin_name == "JasperReports" ]]; then
        local tmp_path="/opt/sisense/storage/external-plugins/tmp"
        local response_stream_jasper=$(kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- bash -c "[[ -d $tmp_path ]] && echo 1 || echo 0")
        local response_jasper=$(echo $response_stream_jasper | grep -oP 0)

        if [[ $response_jasper == 0 ]]; then
            local jasper_msg=" > Path for 'temp' folder doesn't exist, a folder was created:\n  - at: '$tmp_path'\n"
            printf "$jasper_msg"
            kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- mkdir -p "$tmp_path"
        fi

        # Removing RabbitMQ jasperReports/reportReady exchange.
        # During upgrading from 2023.5 to 2023.6 the exchange props are being updated and that couses an error.
        # So its needed to remove the exchange.
        # It will be recreated later by add-on.
        removeJasperReportReadyExchange
    fi

    declare -A component_member_map
    # Call function with argument. Pass array by referance.
    inspect_sources component_member_map
    local sources_path="$package_root_path/$plugin_sources_folder"

    for component in ${inspect_order[@]}; do
        # Check if string is empty.
        if [[ -z component_member_map[$component] ]]; then
            continue
        fi

        for member in ${component_member_map[$component]}; do
            local component_path="$sources_path/$component/"
            local member_path="$component_path/$member"
            local component_destination_path="${destination_paths[$component]}/"
            local msg=" > Copying '$component' component '$member' member:\n  - from: '$component_path'\n  - to: '$component_destination_path'\n"
            printf "$msg"

            local response_stream=$(kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- bash -c "[[ -d $component_destination_path ]] && echo 1 || echo 0")
            local response=$(echo $response_stream | grep -oP 0)

            if [[ $response == 0 ]]; then
                local create_msg=" > Path for '$component' doesn't exist, a folder was created:\n  - at: '$component_destination_path'\n"
                printf "$create_msg"
                kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- mkdir -p "$component_destination_path"
            fi

            kubectl cp -c $DEFAULT_CONTAINER "$member_path" sisense/$PROXY_POD:"$component_destination_path"
        done
    done

    echo " "
    echo " > Add-on components have been installed."
    echo " "

    # Skip this further actions for update.
    if [[ $is_update != "1" ]]; then
        log_pods_restart_message
        echo " > You can check your shiny add-on working in a few minutes. Thanks for choosing Sisense!"
    fi
}

function remove() {
    echo " > Add-on components uninstallation started. Please wait, this can take some time..."
    echo " "

    local skip_restart=$1
    local is_update=$2

    if [[ $plugin_name == "JasperReports" ]]; then
        local tmp_path="/opt/sisense/storage/external-plugins/tmp"
        local response_stream_jasper=$(kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- bash -c "[[ -d $tmp_path ]] && echo 1 || echo 0")
        local response_jasper=$(echo $response_stream_jasper | grep -oP 1)

        if [[ $response_jasper == 1 ]]; then
            local jasper_msg=" > Removing 'temp' folder:\n  - at: '$tmp_path'\n"
            printf "$jasper_msg"
            kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- rm -rf "$tmp_path"
        fi
    fi

    declare -A component_member_map
    # Call function with argument. Pass array by referance.
    inspect_sources component_member_map

    for component in ${inspect_order[@]}; do
        # Check if string is empty.
        if [[ -z component_member_map[$component] ]]; then
            continue
        fi

        local is_partial_remove=$(echo ${REMOVE_BY_NAME_COMPONENT[@]} | grep -oP $component)

        if [[ ! -z $is_partial_remove ]]; then
            local msg=" > Removing '$component' component 'en-US' member: \n  - from: '${destination_paths[$component]}/en-US/'\n"
            printf "$msg"

            exec_script "rm -rf ${destination_paths[$component]}/en-US/*$plugin_name*" "1"

            continue
        fi

        for member in ${component_member_map[$component]}; do
            local member_path="$sources_path/$component/$member"
            local component_destination_path="${destination_paths[$component]}/$member"
            local msg=" > Removing '$component' component '$member' member: \n  - from: '$component_destination_path'\n"
            printf "$msg"

            kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- rm -rf $component_destination_path
        done
    done

    echo " > Add-on components have been uninstalled."
    echo " "

    # Skip this further actions for update.
    if [[ $is_update != "1" ]]; then
        log_pods_restart_message
        echo " > Thanks for flying with Sisense :)"
    fi
}

function verify_installation() {
    echo " > Verifying installation..."
    declare -A component_member_map
    inspect_sources component_member_map

    declare -a result=()
    for component in ${!component_member_map[@]}; do
        for member in ${component_member_map[$component]}; do
            local member_path="${destination_paths[$component]}/$member"
            local msg=" > Checking '$component' component's '$member' member:\n  - from: '$member_path'"
            declare -a dest_check_paths=()

            if [[ $component == 'translations' ]]; then
                local source_member_content=$(ls "$package_root_path/$plugin_sources_folder/$component/$member")
                for translation_part in ${source_member_content[@]}; do
                    dest_check_paths+="$member_path/$translation_part "
                done
            else
                dest_check_paths+=$member_path
            fi

            declare -a response_list=()
            for member_path in ${dest_check_paths[@]}; do
                response_list+=" "
                local response_stream=$(kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- bash -c "[[ -d $member_path || -f $member_path ]] && echo 1 || echo 0")
                response_list+=$(echo $response_stream | grep -oP "\d")
            done

            local zero_response=$(echo $response_list | grep -oP 0)
            if [[ -z $zero_response ]]; then
                printf "$msg \n  - result: OK\n"
                result+="true "
            else
                printf "$msg \n  - result: Warning! Missing component found!\n"
                result+="false "
            fi
        done
    done

    [[ $result == *"false"* ]] && echo " > Add-on verification found missing components. Please re-install the add-on." || echo " > Add-on components are installed correctly."
}

function version() {
    echo $(basename $(dirname "$(pwd)") | grep -oP '[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+' | xargs)
}

function help() {
    echo " > -h - help - show information about commands."
    echo " > -c - check - verify if all add-on components are already installed in proper folders."
    echo " > -i - install - install add-on components."
    echo " > -u - update - update add-on preserving protected files(config.js, config.6.js, config.json) and folders(resources)."
    echo " > -r - remove - remove all add-on components. If there's a configuration saved in MongoDB or in the Configuration Page, it will persist."
    echo " > -v - version - return the version of the add-on from this installation package."
}

function usage() {
    echo " > Type -h to check the list of available commands and their usage."
}

function update() {
    echo " > Add-on components update started."
    backup
    remove "true" "1"
    echo "  "
    install "false" "1"
    restore_backup
    echo " > Add-on components have been updated."
    echo " > You can check your shiny add-on working in a few minutes. Thanks for choosing Sisense!"
}

# --------------------- END PUBLIC ---------------------

# --------------------- PRIVATE ---------------------

function try_unzip_sources() {
    if [ ! -d "$package_root_path/$plugin_sources_folder" ]; then
        echo " > Trying to extract the add-on sources from an archive."

        cd "$package_root_path" && tar -zxf "$plugin_sources_folder.tar.gz"

        echo " > The sources have been extracted successfully."
    fi
}

function backup() {
    echo " "
    find_protected_paths "backup"
    echo " "
}

function restore_backup() {
    echo " "
    find_protected_paths "restore"
    log_pods_restart_message
    echo " "
}

function find_protected_paths() {
    local operation_type=$1
    declare -A component_member_map
    # Call function with argument. Pass array by referance.
    inspect_sources component_member_map

    local backup_folder="$BACKUP_FOLDER"$(date +'%m.%d.%Y')
    local protected_files=$(echo ${PROTECTED_FILES[@]} | tr ' ' '|')
    local protected_folders=$(echo ${PROTECTED_FOLDERS[@]} | tr ' ' '|')
    local ignore_backup_path=$(echo ${EXCLUDE_PTOCTED_PATH[@]} | tr ' ' '|')

    local base_find="find -regextype egrep -regex '.*/($protected_files|$protected_folders)' -print0"

    for component in ${COMPONENTS_WITH_CONFIG[@]}; do
        # Check if string is empty.
        if [[ -z component_member_map[$component] ]]; then
            continue
        fi

        for member in ${component_member_map[$component]}; do
            local component_destination_path="${destination_paths[$component]}/$member"
            local cd_action="cd $component_destination_path"
            local is_component_installed=$(is_path_exist $component_destination_path)

            if [[ $is_component_installed == "false" ]]; then
                continue
            fi

            declare -A protected_list=()
            local find_comand=""$cd_action" && $base_find"

            while IFS= read -r -d $'\0' protected_abs_path; do
                local abs_path=$(echo "$protected_abs_path" | sed 's/\.\//\//' | xargs)

                local path=$(echo $abs_path | grep -oP $ignore_backup_path)

                if [[ ! -z $path ]]; then
                    continue
                fi

                local protected_basename=$(basename ""$protected_abs_path"/")

                protected_list[$protected_basename]=$abs_path

                if [[ ! -d "$package_root_path/$backup_folder/$component/$member" ]]; then
                    mkdir -p "$package_root_path/$backup_folder/$component/$member"
                fi
            done < <(kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- bash -c "$find_comand")

            local info_msg=""
            if [[ ! -z ${!protected_list[@]} ]]; then
                if [[ $operation_type == "backup" ]]; then
                    info_msg=" > Backing up '$component' component '$member' protected members:\n  - to: $package_root_path/$backup_folder/$component/$member \n"
                else
                    info_msg=" > Restoring the backed up '$component' component '$member' protected members:\n  - from: $package_root_path/$backup_folder/$component/$member \n"
                fi
            else
                info_msg=" > No protected files/folders were found for component '$component' '$member' member.\n"
            fi
            printf "$info_msg"

            for protected_item in ${!protected_list[@]}; do
                local source_path=${protected_list[$protected_item]}
                local dir_path=$(dirname $source_path)
                local base=$(basename $source_path)

                local file_type_request="[[ -d "$component_destination_path/$source_path" ]] && echo "folder" || echo "file""
                local path_type=$(exec_script "$file_type_request" "(folder|file)")

                local cp_command=""
                local backup_path="$package_root_path/$backup_folder/$component/$member/"

                if [[ $operation_type == "backup" ]]; then
                    if [[ $path_type == "folder" ]]; then
                        kubectl cp -c $DEFAULT_CONTAINER sisense/$PROXY_POD:"$component_destination_path$source_path" "$backup_path/$protected_item"
                    elif [[ $path_type == "file" ]]; then
                        kubectl cp -c $DEFAULT_CONTAINER sisense/$PROXY_POD:"$component_destination_path$source_path" "$backup_path$dir_path/$base"
                    else
                        echo " > Unknown directory type."
                    fi
                elif [[ $operation_type == "restore" ]]; then
                    if [[ $path_type == "folder" ]]; then
                        kubectl cp -c $DEFAULT_CONTAINER "$backup_path$protected_item/" sisense/$PROXY_POD:"$component_destination_path$dir_path/"
                    elif [[ $path_type == "file" ]]; then
                        kubectl cp -c $DEFAULT_CONTAINER "$backup_path$source_path" sisense/$PROXY_POD:"$component_destination_path$dir_path"
                    else
                        echo " > Unknown directory type."
                    fi
                else
                    echo " > Unknown operation type."
                fi
            done
        done
    done
}

function log_pods_restart_message() {
    echo " > All the relevant pods have been restarted automatically."
}

function is_external_component() {

    local is_external="false"

    if [[ ${!component_member_map[@]} == *"external_addons"* ]]; then
        is_external="true"
    fi

    echo "$is_external"
}

function is_path_exist() {
    local path=$1
    local path_exist="[[ -d "$path" ]] || [[ -f "$path" ]] && echo "true" || echo "false""
    local is_path_exist=$(exec_script "$path_exist" "\d")

    echo is_path_exist
}

function exec_script() {
    local script=$1
    local grep_expr=$2

    local response=$(kubectl exec --stdin --tty -n sisense -c $DEFAULT_CONTAINER $PROXY_POD -- bash -c "$script")
    local msg=$(echo $response | grep -oP "$grep_expr")

    echo "$msg"
}

# --------------------- END PRIVATE ---------------------

while getopts chiurv flag; do
    case "${flag}" in
    c) action="c" ;;
    h) action="h" ;;
    i) action="i" ;;
    u) action="u" ;;
    r) action="r" ;;
    v) action="v" ;;
    *) usage ;;
    esac
done

if [ $action = "i" ]; then
    install
elif [ $action = "c" ]; then
    verify_installation
elif [ $action = "h" ]; then
    help
elif [ $action = "v" ]; then
    version
elif [ $action = "r" ]; then
    remove
elif [ $action = "u" ]; then
    update
else
    usage
fi
