# shellcheck shell=bash
# shellcheck source=scripts/shared/lib/source_only
# shellcheck disable=SC2034 # We declare some shared variables here 
. "${BASH_SOURCE%/*}"/source_only

### Constants ###

readonly KIND_REGISTRY=kind-registry
readonly RESOURCES_DIR=${SCRIPTS_DIR}/resources
readonly OUTPUT_DIR=${DAPPER_OUTPUT}
readonly KUBECONFIGS_DIR=${DAPPER_OUTPUT}/kubeconfigs
readonly SUBM_NS=submariner-operator

### Functions ###

# Mask kubectl to use cluster context if the variable is set and context isn't specified,
# otherwise use the config context as always.
function kubectl() {
    if [[ -n "${cluster}" && ! "${*}" =~ "context" ]]; then
        command kubectl --context="${cluster}" "$@"
    else
        command kubectl "$@"
    fi
}

# Run a command and retry it if it fails, up to the given amount of retries
# 1st argument is the amount of times to retry the command.
# 2nd argument is the command to execute.
# 3rd argument and so forth get passed to the command.
# The current iteration is made available in the iteration variable.
function with_retries() {
    local retries=$1
    local cmnd=$2

    iteration=1; while ((iteration <= retries)); do
        ( $cmnd "${@:3}"; ) &
        if wait $!; then
            return 0
        fi
        ((iteration++))
    done

    echo "Max attempts reached, failed to run '${*:2}'!"
    exit 1
}

# Run a command with the given cluster as the context, which will be unset once finished
# 1st argument is the context which will be set to $cluster global variable.
# 2nd argument is the command to execute.
# 3rd argument and so forth get passed to the command.
function with_context() {
    local cluster=$1
    local cmnd=$2
    $cmnd "${@:3}"
}

# Run cluster commands in parallel.
# 1st argument is the cluster names for which to run.
# 2nd argument is the command to execute, which will have the $cluster variable set.
# 3rd argument and so forth get passed to the command.
function run_parallel() {
    local cmnd=$2
    declare -A pids
    for cluster in $(eval echo "$1"); do
        (
           set -o pipefail
           $cmnd "${@:3}" |& sed "/\[${cluster}]/!s/^/[${cluster}] /"
        ) &
        pids["${cluster}"]=$!
    done

    for pid in "${pids[@]}"; do
        wait $pid
    done
}

# Run the given command (with any arguments) in parallel on all the clusters.
function run_all_clusters() {
    run_parallel "${clusters[*]}" "$@"
}

# Run the given command (with any arguments) in parallel on the clusters that install submariner.
function run_subm_clusters() {
    declare -a subm_clusters
    for cluster in "${clusters[@]}"; do
        [[ "${cluster_subm[${cluster}]}" != "true" ]] || subm_clusters+=( "${cluster}" )
    done

    run_parallel "${subm_clusters[*]}" "$@"
}

function registry_running() {
    docker ps --filter name="^/?$KIND_REGISTRY$" | grep $KIND_REGISTRY
    return $?
}

function add_cluster_cidrs() {
    local val=$1
    local idx=$2
    [[ $globalnet != "true" ]] || val="0"
    cluster_CIDRs[$idx]="10.${val}.0.0/16"
    service_CIDRs[$idx]="100.${val}.0.0/16"
    [[ $globalnet != "true" ]] || global_CIDRs[$idx]="169.254.${1}.0/24"
}

function declare_cidrs() {
    # shellcheck disable=SC2034 # these variables are used elsewhere
    declare -gA cluster_CIDRs service_CIDRs global_CIDRs

    i=1
    for cluster in "${clusters[@]}"; do
        add_cluster_cidrs "$i" "$cluster"
        i=$(("$i"+1))
    done
}

function declare_kubeconfig() {
    source "${SCRIPTS_DIR}"/lib/kubecfg
}

function print_clusters_message() {
    cat << EOM
Your virtual cluster(s) are deployed and working properly and can be accessed with:

export KUBECONFIG=\$(find \$(git rev-parse --show-toplevel)/output/kubeconfigs/ -type f -printf %p:)

$ kubectl config use-context cluster1 # or cluster2, cluster3..

To clean everthing up, just run: make clean-clusters
EOM
}

# Run shell function if it is defined
# 1st argument is the name of the function.
# 2nd argument and so forth get passed to the function.
function run_if_defined() {
    local func=$1
    type -t "${func}" | grep -q function || return 0
    "${func}" "${@:2}"
}
